angularjs - Promise 在 AngularJS Jasmine 测试中解决得太晚了

标签 angularjs unit-testing jasmine

我使用 ng-describe 编写了以下 Jasmine 测试,并使用 karma 运行。

(我正在为 PhantomJS 使用 es6-promise polyfill)

var myModule = angular.module('MyModule', []);
myModule.service('MyService', [function() {
  return {
    getItems: function() {
      // this will be spied and mocked
    }
  };
}]);

myModule.controller('MyController', ['$scope', 'MyService',
  function($scope, MyService) {
    $scope.items = [];

    $scope.refreshItems = function() {
      MyService.getItems().then(
        function ok(items) {
          $scope.items = items;
          console.log('OK, items.length = ' + items.length);
        },
        function fail(reason) {
          console.error('FAIL')
        }).catch(console.error.bind(console));
    };
  }
]);

ngDescribe({
  name: "MyController test",
  modules: ['MyModule'],
  inject: ['MyService', '$rootScope', '$q'],
  controllers: 'MyController',
  tests: function(deps) {

    function getPromise(val) {
      return new Promise(function(resolve, reject) {
        resolve(val);
      });
    }

    it('updates $scope.items', function() {
      spyOn(deps.MyService, 'getItems').and.returnValue(getPromise([4, 5, 6]));
      deps.MyController.refreshItems();
      deps.$rootScope.$digest();

      expect(deps.MyService.getItems).toHaveBeenCalled();
      expect(deps.MyController.items.length).toBe(3);
      console.log("END OF TEST");
    });
  }
});

测试会失败,因为 promise 解决得太晚了:

LOG: 'END OF TEST'
PhantomJS 1.9.8 (Windows 7 0.0.0) MyController test updates $scope.items FAILED
        Expected 0 to be 3.
            at d:/git/myproject/test/controllers/ItmngtControllerTest.js:49
LOG: 'OK, items.length = 3'
PhantomJS 1.9.8 (Windows 7 0.0.0): Executed 37 of 37 (1 FAILED) (0.085 secs / 0.26 secs)

经过长时间的调查,我发现如果我使用 $q 而不是 Promise,它会正常工作。

    function getPromise(val) {
      var deferred = deps.$q.defer();
      deferred.resolve(val);
      return deferred.promise;
    }

但是我想知道为什么会出现这种情况,我可以在测试中更改某些内容以使用 Promise 而不是 $q 来使测试通过吗?

我在很多地方读过有关 $rootScope.$apply() 的内容,但无论我把它放在哪里,它仍然对我不起作用。

最佳答案

Angular $q 测试是同步的,这是一个巨大的优势。一旦调用作用域 $digest(),就可以预期所有 $q promise 链处理程序也会被调用。

另一方面,Promise 通常(包括 ES6 实现)在设计上是异步的。一旦 Promise 被解决,它的处理程序将在下一个tick 时被调用。令人愉快的 Angular 测试不再那么同步了:

it('updates $scope.items', function (done) {
  ...
  setTimeout(() => {
    expect(deps.MyService.getItems).toHaveBeenCalled();
    expect(deps.MyController.items.length).toBe(3);
    console.log("END OF TEST");
    done();
  });
});

Promise 可以被模拟以进行同步测试,mock-promises在这种情况下可以使用。

还有jasmine-co其目的是使用 ES6 生成器和 co 使 Jasmine 中的异步测试变得更容易(实际上确实如此),我不知道它在 ng-describe 中的性能如何。

关于angularjs - Promise 在 AngularJS Jasmine 测试中解决得太晚了,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34228045/

相关文章:

typescript - 是否可以在 Jasmine 单元测试中修改或模拟 Typescript 类使用的 Inversify 容器?

angularjs - AngularJS 中的单元测试 promise 错误 : Expected a spy, 但得到了 getCtrl({ })

angularjs - Bootstrap-突出显示 Active 和 Angular 指令

javascript - 为什么我的列表不会出现使用 ionic?

javascript - 如何使用 JavaScript 获取本地文件夹内容

json - 如何在 Golang 中测试 panic ?

javascript - setTimeout 测试未按预期运行

javascript - 如何将 firebase 的数据与 angularJS 范围绑定(bind)

java - 对事务中需要的 DDL 语句进行单元测试

unit-testing - 使用 redis mock 进行 Redis 单元测试