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

and*_*sit 1 javascript jasmine angularjs

当然,以前有人问过这个问题,但我找不到。我需要模拟一个工厂,但模拟本身需要使用 $q,并且在注入()之后调用模块()时我最终陷入了鸡和蛋的情况。

我看了这个问题,它建议做一个 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);
       }
       );
   }
  }
});
Run Code Online (Sandbox Code Playgroud)

茉莉花测试:

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');
  });

});
Run Code Online (Sandbox Code Playgroud)

我需要我的 mockMyDependencyFactory 来使用 $q,所以我需要将它包装在inject( function(...,我需要在调用module( function ($provide) {...这当然会给我:

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

关于我如何解决这个问题的任何建议?

或者,如果您认为我的设计有缺陷(我想我可以实例化 MyDependencyFactory 并在 MyConsumingFactory 实例化期间传递它而不是使用 angular 的 DI?)我全神贯注:)

Mic*_*nov 5

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

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;
        });
    });

    // ....

});
Run Code Online (Sandbox Code Playgroud)

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

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

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

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