【问题标题】:How to mock AngularJS promises using Jasmine / Karma?如何使用 Jasmine / Karma 模拟 AngularJS 承诺?
【发布时间】:2017-09-04 19:41:54
【问题描述】:

这是我的控制器:

angular.module("AuthenticationApp", ["BaseApp"])
    .controller("AuthCtrl", ["$http", "BaseService", function($http, BaseService) {
        var self = this;

        BaseService.fetch.stuffs()
          .then(function(data) {
            self.stuffs = data;
        });

    }]);

这是我的BaseService(我需要模拟的工厂):

angular.module("BaseApp", ["ngRoute"])

    .factory("BaseService", ["$http", "$q", function($http, $q) {
        var self = this;

        self.fetch = {
         stuffs: function() {
           return $http.get("/api/stuffs/")
             .then(function(response) {
                 return response.data;
           });
         }
        };    

        return self;
    }]);

我不知道从哪里开始。我尝试的是这样的:

describe('Controller: AuthCtrl', function() {
    var ctrl, mockBaseService;

    beforeEach(function() {

        mockBaseService = {
            stuffs: 'test',
            fetch: { 
                stuffs: function() {
                    return {
                      then: function() {
                          return {stuffs: "stuffs"};
                      },
                    }
                },
            },
        };

        spyOn(mockBaseService.fetch, 'stuffs');

        module('BaseApp', function($provide) {
            $provide.value('BaseService', mockBaseService);
        });

        module('AuthenticationApp');

        inject(function($controller) {
            ctrl = $controller('AuthCtrl', {
            });
        });
    });

    it('BaseService.fetch.stuffs should be called on page load', function() {
        expect(mockBaseService.fetch.stuffs).toHaveBeenCalled();
        expect(ctrl.stuffs).toBe({stuffs: "stuffs"});
    });

});

但是当我测试这段代码时,我收到一条错误消息:

TypeError: Cannot read property 'then' of undefined
    at new <anonymous> (/home/user/Documents/ebdjango/ebdjangoapp/static/js/home.js:15:11)

home.js 第 15 行是控制器中的这一行:

       return $http.get("/api/stuffs/")
         .then(function(response) { // line 15
             return response.data;
       });

我如何正确地模拟这个?

编辑:我阅读了 cmets 中提到的文档,并将 mockBaseService 更改为:

describe('Controller: AuthCtrl', function() {
    var ctrl, mockBaseService;

    beforeEach(function() {
        // create promise
        var deferred = $q.defer();
        var promise = deferred.promise;
        // make promise.then return {stuffs: "stuffs"}
        promise.then(function(value) { return {stuffs: "stuffs" };  });

        mockBaseService = {
            stuffs: 'stuffs',
            fetch: { 
                stuffs: function() {
                    return promise;
                },
            },
        };

        spyOn(mockBaseService.fetch, 'stuffs');

        module('BaseApp', function($provide) {
            $provide.value('BaseService', mockBaseService);
        });

        module('AuthenticationApp');

        inject(function($controller) {
            ctrl = $controller('AuthCtrl', {
            });
        });
    });

    it('BaseService.fetch.stuffs should be called on page load', inject(function($rootScope) {
        deferred.resolve('test');
        $rootScope.$apply();
        expect(mockBaseService.fetch.stuffs).toHaveBeenCalled();
        expect(ctrl.stuffs).toBe({stuffs: "stuffs"});
    }));

});

但这会返回ReferenceError: $q is not defined 错误。我尝试像这样注入_$q_

beforeEach(inject(function(_$q_) {
    $q = _$q_;
    // ... everything else goes here... //

然后我收到一条错误消息Error: Injector already created, can not register a module!。这里:injector already created. can not register a module 表示如果我们在inject($someDependency) 之前执行module('someApp'),则可以解决此问题。但是我做不到

module('BaseApp', function($provide) {
    $provide.value('BaseService', mockBaseService);
});

在注入_$q_ 之前,因为$q 用于mockBaseService。我从这里去哪里?

【问题讨论】:

标签: angularjs angular-promise angular-mock


【解决方案1】:

模拟我经常做的承诺

1) spyOn(service,'method').andCallFake( $q.when( Fixture.data() ) )

不要创建那个 mockBaseService,我以前做过,而且它有很多东西要白写 也可以使用内置的 AngularJS 拦截 $http 即

2) $httpBackend.when('GET', '/url').respond(Fixture.data())

我绝对更喜欢 1) 因为我喜欢在进行测试时将自己隔离在某一层。我想这归结为品味问题

【讨论】:

  • 好的,谢谢。顺便说一句,示例 1 中的 FixtureFixture.data() 到底是什么?
  • 哦,这只是我自己的事情......它是您响应的数据。一个很好的做法是在一个夹具中的所有响应数据都像这样 function Fixture(){ this.user ={ name : 'Adam' }; this.accounts = [ { 余额 : 100 } ] }
  • 哦,好吧。所以我尝试做你提到的(即在 beforeEach 中添加以下内容:function Fixture() { ... } 然后将 Fixture.data() 传递给 and.callFake( $q.when( Fixture.data() ) ))但我收到一条错误消息,提示 Fixture.data 不是函数。知道如何解决这个问题吗?
  • 这里是一个 jsbin,其中包含三个如何创建夹具的选项,jsbin.com/xepaxaw/edit?html,js,console,output
  • 当我执行and.callFake( $q.when( Fixture.data()) ) 时,我收到一条错误消息Argument passed to callFake should be a function, got [object Object]。我正在使用如何创建夹具的第一个选项,但第二个选项也给出了相同的错误。编辑:基于这里的答案:stackoverflow.com/questions/24997174/… 也许这与$q.when 成为一个函数而不是传递给and.callFake() 的值有关?我不确定如何解决此问题。
猜你喜欢
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
  • 2017-06-03
  • 2020-04-05
  • 1970-01-01
  • 1970-01-01
  • 2016-02-26
  • 2014-09-14
相关资源
最近更新 更多