使用Jasmine对承诺链进行单元测试

问题描述 投票:0回答:2

我想为有许多承诺链的工厂编写单元测试。以下是我的代码片段:

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

但结果仍然相同。我的问题是我需要多次解决,即每个承诺。我怎么能等到所有的承诺都执行完毕。

javascript angularjs unit-testing karma-jasmine angular-promise
2个回答
0
投票

方法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(函数没有阻塞)以及处理程序如何被放入队列。这将为您节省很多麻烦。


0
投票

我做了以下修改以使其正常工作。我错过了对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调用做的类似。

© www.soinside.com 2019 - 2024. All rights reserved.