如何在AngularJS Jasmine单元测试中模拟返回承诺的服务?

我有那个用途,它可以发出远程呼叫,返回承诺:myServicemyOtherService

angular.module('app.myService', ['app.myOtherService'])
  .factory('myService', [
    myOtherService,
    function(myOtherService) {
      function makeRemoteCall() {
        return myOtherService.makeRemoteCallReturningPromise();
      }

      return {
        makeRemoteCall: makeRemoteCall
      };      
    }
  ])

要为 I 进行单元测试,需要 mock ,这样它的方法返回一个 promise。我是这样做的:myServicemyOtherServicemakeRemoteCallReturningPromise

describe('Testing remote call returning promise', function() {
  var myService;
  var myOtherServiceMock = {};

  beforeEach(module('app.myService'));

  // I have to inject mock when calling module(),
  // and module() should come before any inject()
  beforeEach(module(function ($provide) {
    $provide.value('myOtherService', myOtherServiceMock);
  }));

  // However, in order to properly construct my mock
  // I need $q, which can give me a promise
  beforeEach(inject(function(_myService_, $q){
    myService = _myService_;
    myOtherServiceMock = {
      makeRemoteCallReturningPromise: function() {
        var deferred = $q.defer();

        deferred.resolve('Remote call result');

        return deferred.promise;
      }    
    };
  }

  // Here the value of myOtherServiceMock is not
  // updated, and it is still {}
  it('can do remote call', inject(function() {
    myService.makeRemoteCall() // Error: makeRemoteCall() is not defined on {}
      .then(function() {
        console.log('Success');
      });    
  }));  

从上面可以看出,我的模拟的定义取决于 ,我必须使用 加载它。此外,注入模拟应该发生在 中,这应该在 前面。但是,一旦我更改了模拟的值,它就不会更新。$qinject()module()inject()

正确的方法是什么?


答案 1

我不确定为什么你这样做的方式不起作用,但我通常使用该函数来执行此操作。像这样:spyOn

describe('Testing remote call returning promise', function() {
  var myService;

  beforeEach(module('app.myService'));

  beforeEach(inject( function(_myService_, myOtherService, $q){
    myService = _myService_;
    spyOn(myOtherService, "makeRemoteCallReturningPromise").and.callFake(function() {
        var deferred = $q.defer();
        deferred.resolve('Remote call result');
        return deferred.promise;
    });
  }

  it('can do remote call', inject(function() {
    myService.makeRemoteCall()
      .then(function() {
        console.log('Success');
      });    
  }));

还要记住,您需要调用该函数才能被调用。请参阅$q文档的“测试”部分。$digestthen

------编辑------

在仔细查看了您正在执行的操作之后,我想我在您的代码中看到了问题。在 中,您将设置为一个全新的对象。永远不会看到此引用。您只需要更新现有引用:beforeEachmyOtherServiceMock$provide

beforeEach(inject( function(_myService_, $q){
    myService = _myService_;
    myOtherServiceMock.makeRemoteCallReturningPromise = function() {
        var deferred = $q.defer();
        deferred.resolve('Remote call result');
        return deferred.promise;   
    };
  }

答案 2

我们也可以写茉莉花直接通过间谍返回承诺的实现。

spyOn(myOtherService, "makeRemoteCallReturningPromise").andReturn($q.when({}));

对于茉莉花2:

spyOn(myOtherService, "makeRemoteCallReturningPromise").and.returnValue($q.when({}));

(抄自评论,感谢ccnokes)