一尘不染

Angular.js中基于单元测试诺言的代码

angularjs

我在尝试在Angularjs中测试基于承诺的代码时遇到了困难。

我的控制器中有以下代码:

    $scope.markAsDone = function(taskId) {
        tasksService.removeAndGetNext(taskId).then(function(nextTask) {
            goTo(nextTask);
        })
    };

    function goTo(nextTask) {
        $location.path(...);
    }

我想对以下情况进行单元测试:

  • 什么时候markAsDone被调用tasksService.removeAndGetNext
  • tasksService.removeAndGetNext是做了应该改变的位置(调用goTo

在我看来,没有简单的方法可以分别测试这两种情况。

我要做的第一个测试是:

var noopPromise= {then: function() {}}
spyOn(tasksService, 'removeAndGetNext').andReturn(noopPromise);

现在,要测试第二种情况,我需要创建另一个永远不变的虚假承诺resolved。这一切都非常乏味,并且有很多样板代码。

还有其他测试方法吗?还是我的设计有异味?


阅读 228

收藏
2020-07-04

共1个答案

一尘不染

您仍然需要模拟服务并返回承诺,但是您应该使用真实的承诺,因此您无需实现其功能。使用beforeEach创建已被兑现承诺,并嘲笑服务,如果你需要它总是被解决。

var $rootScope;

beforeEach(inject(function(_$rootScope_, $q) {
  $rootScope = _$rootScope_;

  var deferred = $q.defer();
  deferred.resolve('somevalue'); //  always resolved, you can do it from your spec

  // jasmine 2.0
  spyOn(tasksService, 'removeAndGetNext').and.returnValue(deferred.promise);

  // jasmine 1.3
  //spyOn(tasksService, 'removeAndGetNext').andReturn(deferred.promise);

}));

如果您希望在每个it块中使用不同的值来解析它,则只需将延期公开给局部变量并在规范中对其进行解析。

当然,您可以按原样保留测试,但是这里有一些非常简单的规范可以向您展示它如何工作。

it ('should test receive the fulfilled promise', function() {
  var result;

  tasksService.removeAndGetNext().then(function(returnFromPromise) {
    result = returnFromPromise;
  });

  $rootScope.$apply(); // promises are resolved/dispatched only on next $digest cycle
  expect(result).toBe('somevalue');
});
2020-07-04