【问题标题】:Truly mocking providers真正嘲笑提供者
【发布时间】:2015-06-25 10:15:06
【问题描述】:

所以基本上没有关于如何省略模块上的 config/run/provider 块而不为单元测试做一些文件加载​​排序黑客的信息。假设如下:

// dependency chain for providers:
// aaa <- bbb <- ccc


angular.module('module0').provider('bbb', [
  'aaaProvider', 
  function (aaaProvider) {
    // aaaProvider definition
    ...
  });

angular.module('module1').provider('ccc', [
  'bbbProvider',
  function (bbbProvider) {
    // bbbProvider definition
    ...
  });

angular.module('module1').controller('controller', [
  function () {
    // controller definition
  }]);

现在假设我们正在为controller 编写单元测试。我们该怎么做?

module('module1');
inject(function ($controller, $rootScope) {
  ...
});

糟糕,我们遇到了问题。 module('module1') 将触发ccc 的提供者定义,它依赖于bbb,它依赖于aaa。因此,除非我们阻止在 module1 上触发 ccc 的提供程序定义,否则我们将运行与 controller 无关的代码,我们正在单元测试。

我们可以使用module(function ($provide) { ... }); 表示法模拟bbb,这样我们就根本不需要加载module0

但这并不能解决ccc 的问题。我无法阻止它运行。

问题:有没有办法阻止module1运行ccc提供者的定义,这与controller无关,我们正在单元测试?

到目前为止我尝试过的事情:

  • 在单元测试中覆盖定义:

    angular.module('module1').provider('ccc', [function () {}]);

【问题讨论】:

    标签: angularjs unit-testing


    【解决方案1】:

    首先你需要在你的 app.js 中创建两个模块

    module1internal - 该模块没有依赖关系,将用于所有 您的应用程序提供商

    angular.module('module1internal', []);
    

    module1 - 这个模块依赖于 module1internal 并且将包括所有其他依赖项、配置和运行块

    angular.module('module1', ['module1internal','bbbProvider']);
    

    在您的应用中,您应该只使用内部模块,而不是

    angular.module('module1').controller('controller', [
      function () {
        // controller definition
      }]);
    

    你应该使用

    angular.module('module1internal').controller('controller', [
          function () {
            // controller definition
          }]);
    

    测试将如下所示:

    'use strict';
    
    describe('Controller: TheController', function () {
    
      // mock second level dependencies
      beforeEach(function () {
        module('module1internal');
        module({
          bbb: {}
        });
      });
    
      var TheController, scope,
        cccMock;
    
      // mock injected providers
      beforeEach(inject(function (_ccc_) {
        cccMock = mock(_ccc_);
      }));
    
    
      // Initialise the controller and mock scope 
      beforeEach(inject(function ($controller, $rootScope) {
        scope = $rootScope.$new();
        TheController = $controller('TheController', {
          $scope: scope
        });
    
      }));
    
      it(' should call ccc functionA on functionThatUsesFunctionA call', function () {
        TheController.functionThatUsesFunctionA("someValue");
        expect(cccMock.functionA).toHaveBeenCalledWith("someValue");
      });
    
    
    
      //create spy on all provider's methods(mock it)
      function mock(angularServiceToMock) {
    
        for (var i = 0; i < Object.getOwnPropertyNames(angularServiceToMock).length; i++) {
          spyOn(angularServiceToMock, Object.getOwnPropertyNames(angularServiceToMock)[i]);
        }
        return angularServiceToMock;
      }
    
    });
    

    【讨论】:

    • 这是一个可行的解决方案,尽管它对我来说意味着大量的重构(这是一个包含数百个控制器、指令、提供程序运行块等的巨大应用程序)
    • 这是新项目的最佳实践建议。
    • 嗯,这个问题已经有一段时间了。使用这种方法,您如何在 module1internal 内对各个提供程序进行单元测试,而不在模块中运行其他提供程序?
    • 我不确定我是否理解这个问题,在这种方法中,有两种方法可以防止提供程序在测试中运行。 1)通过将所有公共方法转换为 spy 对象来禁用它们(示例中的函数 mock()) 2)通过将提供者添加到模块(示例中的 bbb{})来覆盖提供者,第二个在您有 init 时很有用你想阻止运行的onload。
    • 我的问题是,如果我们将数千个提供者放入一个模块中,并且如果我们只想测试 1 个,我们如何在不明确禁用其他 999 个提供者的情况下做到这一点?
    猜你喜欢
    • 2019-09-08
    • 2023-02-08
    • 1970-01-01
    • 2014-10-27
    • 2016-10-02
    • 2019-12-20
    • 1970-01-01
    • 2019-02-15
    • 2010-09-23
    相关资源
    最近更新 更多