在Jasmine中使用$q的模拟棱角工厂

Mock angular factory that uses $q in Jasmine

本文关键字:模拟 工厂 Jasmine      更新时间:2023-09-26

以前肯定有人问过这个问题,但我找不到。我需要模拟一个工厂,但模拟本身需要使用$q,并且在inject()之后调用module()时,我最终陷入了鸡和蛋的境地。

我看了这个问题,它建议做一个spyOn,它适用于服务,因为它是一个单例,但我在工厂返回的函数上调用new

var app = angular.module('app', []);
app.factory('MyDependencyFactory', function() {
  return function() {
    this.doPromise = function () {
      var defer = $q.defer();
      //obviously more complicated.
      defer.resolve();
      return defer.promise;   
    }
  }
});
app.factory('MyConsumingFactory', function(MyDependencyFactory) {
 return function() {
   var dependency = new MyDependencyFactory();
   this.result;
   this.doSomething = function () {
     dependency.doPromise().then(
       function (data) {
         this.result = data;
       },
       function (error) {
         console.log(error);
       }
       );
   }
  }
});

茉莉花测试:

describe('MyConsumingFactory', function() {
  var MyConsumingFactory;
  beforeEach(function () {
    module('app');
    inject( function (_MyConsumingFactory_) {
      MyConsumingFactory = _MyConsumingFactory_;
    });
    inject( function ($q) {
      mockMyDependencyFactory = function () {
        this.doPromise = function (data) {
            var defer = $q.defer();
            defer.resolve('mock data');
          };
        };
    });
    module( function ($provide) {
      $provide.factory('MyDependencyFactory', mockMyDependencyFactory);
    });
  });
  it('works correctly', function () {
    MyConsumingFactory.doSomething();
    $rootScope.$apply();
    expect(MyConsumingFactory.result).toEqual('mock data');
  });
});

我需要我的mockMyDependencyFactory来使用$q,所以我需要将其封装在inject(函数(…)中,并且我需要在调用模块(函数($provide){…

错误:注入器已创建,无法注册模块

有什么建议可以帮我解决这个问题吗?

或者,如果你认为我的设计有缺陷(我想我可以实例化一个MyDependencyFactory,并在MyConsumingFactory的实例化过程中传递它,而不是使用angular的DI?)我洗耳恭听:)

首先,您对module()的所有调用都应该在inject()之前,否则您将得到以下错误:Injector already created, can not register a module!,即您应该在将模块注入代码之前注册模块。知道了这一点,我们需要在注入之前模拟MyDependencyFactory,但如果$q只在inject()中可用,我们如何在其中获得它?实际上,在角度测试中,将注入的服务分配给测试套件中的全局变量,然后在所有场景中使用它是一种常见的技术:

describe('some suite', function () {
    // "global" variables for injected services
    var $rootScope, $q;
    beforeEach(function () {
        module('app');
        module(function($provide) {
            $provide.factory('MyDependencyFactory', function () {
                return function () {
                    this.doPromise = function (data) {
                        // use "globals"
                        var defer = $q.defer();
                        defer.resolve('mock data');
                        return defer.promise;
                    };
                };   
            });
        });
        inject(function (_$rootScope_, _$q_) {
            // assign to "globals"
            $rootScope = _$rootScope;
            $q = _$q;
        });
    });
    // ....
});

可以在$provide块中使用$q的原因是它不会立即使用,只有在调用模拟方法或创建模拟对象的实例时才会使用。到那时,它将被注入并分配给全局变量$q,并具有适当的值。

如果你想用不同的值多次解析你的承诺,你还可以做一个技巧,那就是创建一个全局变量defer,并不是在特定的方法中初始化它,而是在某个beforeEach块中初始化,然后在你的场景中用你在这个特定场景中想要的值来执行defer.resolve('something')

在这里你可以看到你的代码的一个工作示例,我做了一些额外的修复来使它工作(有注释)。

注意:我说的是"全局"变量,但它实际上并不像JS术语中那样是全局的,而是特定测试套件中的全局变量。