我想为有许多承诺链的工厂编写单元测试。以下是我的代码片段:
angular.module('myServices',[])
.factory( "myService",
['$q','someOtherService1', 'someOtherService2', 'someOtherService3', 'someOtherService4',
function($q, someOtherService1, someOtherService2, someOtherService3, someOtherService4) {
method1{
method2().then(
function(){ someOtherService3.method3();},
function(error){/*log error;*/}
);
return true;
};
var method2 = function(){
var defer = $q.defer();
var chainPromise = null;
angular.forEach(myObject,function(value, key){
if(chainPromise){
chainPromise = chainPromise.then(
function(){return method4(key, value.data);},
function(error){/*log error*/});
}else{
chainPromise = method4(key, value.data);
}
});
chainPromise.then(
function(){defer.resolve();},
function(error){defer.reject(error);}
);
return defer.promise;
};
function method4(arg1, arg2){
var defer = $q.defer();
someOtherService4.method5(
function(data) {defer.resolve();},
function(error) {defer.reject(error);},
[arg1,arg2]
);
return defer.promise;
};
var method6 = function(){
method1();
};
return{
method6:method6,
method4:method4
};
}]);
为了测试它,我为所有服务创建了间谍对象,但提到了有问题的对象
beforeEach( function() {
someOtherService4Spy = jasmine.createSpyObj('someOtherService4', ['method4']);
someOtherService4Spy.method4.andCallFake(
function(successCallback, errorCallback, data) {
// var deferred = $q.defer();
var error = function (errorCallback) { return error;}
var success = function (successCallback) {
deferred.resolve();
return success;
}
return { success: success, error: error};
}
);
module(function($provide) {
$provide.value('someOtherService4', someOtherService4);
});
inject( function(_myService_, $injector, _$rootScope_,_$q_){
myService = _myService_;
$q = _$q_;
$rootScope = _$rootScope_;
deferred = _$q_.defer();
});
});
it("test method6", function() {
myService.method6();
var expected = expected;
$rootScope.$digest();
expect(someOtherService3.method3.mostRecentCall.args[0]).toEqualXml(expected);
expect(someOtherService4Spy.method4).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), [arg,arg]);
expect(someOtherService4Spy.method4).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), [arg,arg]);
});
它显示错误
。期待(someOtherService3.method3.mostRecentCall.args [0])toEqualXml(预期);
调试后我发现它没有等待任何解析的承诺,所以方法1返回true,甚至没有执行method3。我甚至尝试过
someOtherService4Spy.method4.andReturn(function(){return deferred.promise;});
但结果仍然相同。我的问题是我需要多次解决,即每个承诺。我怎么能等到所有的承诺都执行完毕。
方法1不返回promise,所以你怎么知道它调用的异步函数已经完成。相反,你应该返回:
return method2().then(
method6调用异步函数但又不返回一个promise(它返回undefined)所以你怎么知道它已经完成了?你应该回来:
return method1();
在测试中你应该mock $q
并让它解决或拒绝一个值,但我想不出你为什么会有一个不返回任何东西的异步函数的原因,因为你不知道它是否失败以及它何时失败完成。
方法2可以用更稳定的方式编写,因为如果神奇地出现的myObject是空的({}
或[]
)它当前会崩溃
var method2 = function(){
var defer = $q.defer();
var keys = Object.keys(myObject);
return keys.reduce(
function(acc,item,index){
return acc.then(
function(){return method4(keys[index],myObject[key].data);},
function(err){console.log("error calling method4:",err,key,myObject[key]);}
)
}
,$q.defer().resolve()
)
};
并且尽量不要在函数中出现神奇的变量,这可能是一个全局变量,但是你的代码没有显示它来自哪里,我怀疑是否需要将这个变量放在你的函数之外而不是传递给功能。
你可以了解更多关于promises here你应该理解为什么一个函数返回一个promise(函数没有阻塞)以及处理程序如何被放入队列。这将为您节省很多麻烦。
我做了以下修改以使其正常工作。我错过了对method5的请求的处理,因为它处于挂起状态。一旦我处理了方法5的所有请求并提供了successCallback(同时调用了digest()),它就开始工作了。
someOtherService4Spy.responseArray = {};
someOtherService4Spy.requests = [];
someOtherService4Spy.Method4.andCallFake( function(successCallback, errorCallback, data){
var request = {data:data, successCallback: successCallback, errorCallback: errorCallback};
someOtherService4Spy.requests.push(request);
var error = function(errorCallback) {
request.errorCallback = errorCallback;
}
var success = function(successCallback) {
request.successCallback = successCallback;
return {error: error};
}
return { success: success, error: error};
});
someOtherService4Spy.flush = function() {
while(someOtherService4Spy.requests.length > 0) {
var cachedRequests = someOtherService4Spy.requests;
someOtherService4Spy.requests = [];
cachedRequests.forEach(function (request) {
if (someOtherService4Spy.responseArray[request.data[1]]) {
request.successCallback(someOtherService4Spy.responseArray[request.data[1]]);
} else {
request.errorCallback(undefined);
}
$rootScope.$digest();
});
}
}
然后我将我的测试修改为:
it("test method6", function() {
myService.method6();
var expected = expected;
var dataDict = {data1:"data1", data2:"data2"};
for (var data in dataDict) {
if (dataDict.hasOwnProperty(data)) {
someOtherService4Spy.responseArray[dataDict[data]] = dataDict[data];
}
}
someOtherService4Spy.flush();
expect(someOtherService3.method3.mostRecentCall.args[0]).toEqualXml(expected);
expect(someOtherService4Spy.method4).toHaveBeenCalledWith(jasmine.any(Function), jasmine.any(Function), [arg,arg]);
});
这符合我的期望。由于承诺链而我正在考虑这个问题但是当我处理method5回调方法时,它得到了解决。我得到了刷新请求的想法,就像我为http调用做的类似。