Angularjs中基于单元测试承诺的代码

时间:2013-04-18 11:22:31

标签: javascript angularjs promise

我很难在Angularjs中尝试测试基于promise的代码。

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

    $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的假承诺。这一切都非常乏味,而且它是很多样板代码。

还有其他方法可以测试这些东西吗?或者我的设计有异味?

3 个答案:

答案 0 :(得分:107)

您仍然需要模拟服务并返回承诺,但您应该使用真正的承诺,因此您不需要实现其功能。使用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');
});

答案 1 :(得分:4)

另一种方法是直接从我正在测试的控制器中取出:

var create_mock_promise_resolves = function (data) {
    return { then: function (resolve) { return resolve(data); };
};

var create_mock_promise_rejects = function (data) {
    return { then: function (resolve, reject) { if (typeof reject === 'function') { return resolve(data); } };
};

var create_noop_promise = function (data) {
    return { then: function () { return this; } };
};

答案 2 :(得分:0)

另外一个选项是,您可以避免使用Q库(https://github.com/kriskowal/q)作为$digest的替代品来调用$q,例如:

beforeEach(function () {
    module('Module', function ($provide) {
        $provide.value('$q', Q); 
    });
});

这种方式可以在$ digest循环之外解析/拒绝承诺。