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

Javascript Angularjs中基于单元测试承诺的代码,javascript,angularjs,promise,Javascript,Angularjs,Promise,我很难用Angularjs测试基于承诺的代码 我的控制器中有以下代码: $scope.markAsDone = function(taskId) { tasksService.removeAndGetNext(taskId).then(function(nextTask) { goTo(nextTask); }) }; function goTo(nextTask) { $location.path

我很难用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);
现在,为了测试第二种情况,我需要创建另一个总是
解决的假承诺。这一切都相当乏味,而且有很多样板代码


有没有其他方法来测试这些东西?还是我的设计有味道?

您仍然需要模拟服务并返回承诺,但您应该使用真正的承诺,因此不需要实现其功能。在每次之前使用
创建已经履行的承诺,并在需要始终解决该问题时模拟服务

var$rootScope;
beforeach(注入(函数($rootScope_uq)){
$rootScope=\u$rootScope;
var deferred=$q.deferred();
deferred.resolve('somevalue');//始终解析,可以从规范中执行
//茉莉花2.0
spyOn(tasksService,'removeAndGetNext')。和.returnValue(deferred.promise);
//茉莉花1.3
//spyOn(tasksService,'removeAndGetNext').andReturn(deferred.promise);
}));
如果您更愿意在每个
it
块中使用不同的值来解析它,那么您只需将deferred暴露给局部变量,并在spec中解析它

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

it('should test receive the completed promise',function(){
var结果;
tasksService.removeAndGetNext().then(函数(returnFromPromise){
结果=承诺的回报;
});
$rootScope.$apply();//承诺仅在下一个$digest周期中解析/分派
期望(结果)。toBe('somevalue');
});

您仍然需要模拟服务并返回承诺,但您应该使用真正的承诺,因此不需要实现其功能。在每次之前使用
创建已经履行的承诺,并在需要始终解决该问题时模拟服务

var$rootScope;
beforeach(注入(函数($rootScope_uq)){
$rootScope=\u$rootScope;
var deferred=$q.deferred();
deferred.resolve('somevalue');//始终解析,可以从规范中执行
//茉莉花2.0
spyOn(tasksService,'removeAndGetNext')。和.returnValue(deferred.promise);
//茉莉花1.3
//spyOn(tasksService,'removeAndGetNext').andReturn(deferred.promise);
}));
如果您更愿意在每个
it
块中使用不同的值来解析它,那么您只需将deferred暴露给局部变量,并在spec中解析它

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

it('should test receive the completed promise',function(){
var结果;
tasksService.removeAndGetNext().then(函数(returnFromPromise){
结果=承诺的回报;
});
$rootScope.$apply();//承诺仅在下一个$digest周期中解析/分派
期望(结果)。toBe('somevalue');
});

另一种方法是直接从我正在测试的控制器中选择以下方法:

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; } };
};

另一种方法是直接从我正在测试的控制器中提取以下内容:

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; } };
};

另一个选择是,您可以使用Q库()作为
$Q
的替代品,从而避免调用
$digest
,例如:

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

这种方式可以在$digest周期之外解决/拒绝承诺。

并且,作为另一种选择,您可以通过使用Q库()作为
$Q
的替代品来避免调用
$digest
,例如:

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

这样,承诺可以在$digest周期之外得到解决/拒绝。

您能解释一下$rootScope=$rootScope的用途吗;在第一个示例中,我所做的是注入
$rootScope
(如果在前后插入一个uu,它将被忽略)并通过局部变量公开它。这样,您就不需要将它注入到每个单独的规范中,因为他们可能都会使用它。+1我缺少$rootScope。$apply()-非常有用thanks@CaioToOn是否有一些方法可以在不应用范围的情况下发送承诺?对于记录,整个
var deferred=$q.deferred();推迟。解决(‘某事’);returnValue(deferred.promise)
可以减少为
returnValue($q.when('something'))
你能解释一下$rootScope=$rootScope的用途吗;在第一个示例中,我所做的是注入
$rootScope
(如果在前后插入一个uu,它将被忽略)并通过局部变量公开它。这样,您就不需要将它注入到每个单独的规范中,因为他们可能都会使用它。+1我缺少$rootScope。$apply()-非常有用thanks@CaioToOn是否有一些方法可以在不应用范围的情况下发送承诺?对于记录,整个
var deferred=$q.deferred();推迟。解决(‘某事’);returnValue(deferred.promise)
可以减少到
returnValue($q.when('something'))
接受的解决方案对我不起作用。事实就是这样:被接受的解决方案对我不起作用。确实如此:promise mock将同步运行resolve()或reject()函数,但在AngularJs中,它始终是异步运行的。所以我不推荐这种解决方案,因为它不能准确地测试代码。在单元测试中,您希望能够同步运行异步代码。这就是为什么像
$httpBackend
这样的服务会在Angular testi中自动模拟出来