我正在尝试在控制器中设置超时,以便如果在250毫秒内未收到响应,它将失败。我将单元测试的超时设置为10000,这样就可以满足此条件,有人可以指出正确的方向吗?(编辑,我试图在不使用$ http服务的情况下实现这一点,我知道该服务提供超时功能)
(编辑- 我的其他单元测试失败了,因为我没有在它们上调用timeout.flush,现在我只需要在promiseService.getPromise()返回未定义的promise时获得超时消息即可。我删除了问题的早期代码)。
promiseService(promise是一个测试套件变量,允许我在应用之前对每个测试套件中的promise使用不同的行为,例如拒绝一个,另一个成功)
mockPromiseService = jasmine.createSpyObj('promiseService', ['getPromise']); mockPromiseService.getPromise.andCallFake( function() { promise = $q.defer(); return promise.promise; })
正在测试的控制器功能-
$scope.qPromiseCall = function() { var timeoutdata = null; $timeout(function() { promise = promiseService.getPromise(); promise.then(function (data) { timeoutdata = data; if (data == "promise success!") { console.log("success"); } else { console.log("function failure"); } }, function (error) { console.log("promise failure") } ) }, 250).then(function (data) { if(typeof timeoutdata === "undefined" ) { console.log("Timed out") } },function( error ){ console.log("timed out!"); }); }
测试(通常我在这里解决或拒绝诺言,但未设置它就是模拟超时)
it('Timeout logs promise failure', function(){ spyOn(console, 'log'); scope.qPromiseCall(); $timeout.flush(251); $rootScope.$apply(); expect(console.log).toHaveBeenCalledWith("Timed out"); })
首先,我想说你的控制器实现应该是这样的:
$scope.qPromiseCall = function() { var timeoutPromise = $timeout(function() { canceler.resolve(); //aborts the request when timed out console.log("Timed out"); }, 250); //we set a timeout for 250ms and store the promise in order to be cancelled later if the data does not arrive within 250ms var canceler = $q.defer(); $http.get("data.js", {timeout: canceler.promise} ).success(function(data){ console.log(data); $timeout.cancel(timeoutPromise); //cancel the timer when we get a response within 250ms }); }
您的测试:
it('Timeout occurs', function() { spyOn(console, 'log'); $scope.qPromiseCall(); $timeout.flush(251); //timeout occurs after 251ms //there is no http response to flush because we cancel the response in our code. Trying to call $httpBackend.flush(); will throw an exception and fail the test $scope.$apply(); expect(console.log).toHaveBeenCalledWith("Timed out"); }) it('Timeout does not occur', function() { spyOn(console, 'log'); $scope.qPromiseCall(); $timeout.flush(230); //set the timeout to occur after 230ms $httpBackend.flush(); //the response arrives before the timeout $scope.$apply(); expect(console.log).not.toHaveBeenCalledWith("Timed out"); })
演示
另一个例子promiseService.getPromise:
promiseService.getPromise
app.factory("promiseService", function($q,$timeout,$http) { return { getPromise: function() { var timeoutPromise = $timeout(function() { console.log("Timed out"); defer.reject("Timed out"); //reject the service in case of timeout }, 250); var defer = $q.defer();//in a real implementation, we would call an async function and // resolve the promise after the async function finishes $timeout(function(data){//simulating an asynch function. In your app, it could be // $http or something else (this external service should be injected //so that we can mock it in unit testing) $timeout.cancel(timeoutPromise); //cancel the timeout defer.resolve(data); }); return defer.promise; } }; }); app.controller('MainCtrl', function($scope, $timeout, promiseService) { $scope.qPromiseCall = function() { promiseService.getPromise().then(function(data) { console.log(data); });//you could pass a second callback to handle error cases including timeout } });
您的测试类似于上面的示例:
it('Timeout occurs', function() { spyOn(console, 'log'); spyOn($timeout, 'cancel'); $scope.qPromiseCall(); $timeout.flush(251); //set it to timeout $scope.$apply(); expect(console.log).toHaveBeenCalledWith("Timed out"); //expect($timeout.cancel).not.toHaveBeenCalled(); //I also use $timeout to simulate in the code so I cannot check it here because the $timeout is flushed //In real app, it is a different service }) it('Timeout does not occur', function() { spyOn(console, 'log'); spyOn($timeout, 'cancel'); $scope.qPromiseCall(); $timeout.flush(230);//not timeout $scope.$apply(); expect(console.log).not.toHaveBeenCalledWith("Timed out"); expect($timeout.cancel).toHaveBeenCalled(); //also need to check whether cancel is called })