在我的AngularJS应用程序中,我无法弄清楚如何进行单元测试,然后承诺的执行会改变location.url。我有一个功能登录,它可以调用服务 AuthenticationService 。它返回promise,而then promise的执行会改变location.url。
这是控制器 AuthCtrl :
angular
.module('bloc1App')
.controller('AuthCtrl', ['$scope', '$http','$location', 'AuthenticationService',
function($scope, $http, $location, AuthenticationService) {
this.AuthenticationService = AuthenticationService;
var self = this;
$scope.login = function(username, password){
self.AuthenticationService
.login(username, password)
.then(function(){
$location.url('/tasks');
});
};
}
]);
这是我在单元测试中的尝试:
describe('Controller: AuthCtrl', function () {
var scope,
location,
route,
q,
rootScope,
AuthCtrl;
beforeEach(module('bloc1App'));
beforeEach(inject(function($controller, $rootScope, $location, $route, $q) {
scope = $rootScope.$new();
q = $q;
rootScope = $rootScope;
location = $location;
route = $route;
AuthCtrl = $controller('AuthCtrl', {
$scope: scope,
$route: route,
$location: location
});
}));
it('login should redirect user to tasks view', function(){
var deferred = q.defer();
spyOn(AuthCtrl.AuthenticationService, 'login').andReturn(deferred.promise);
spyOn(location, 'url');
scope.login('foo', 'foo');
deferred.resolve();
scope.$apply();
expect(location.url).toHaveBeenCalledWith('/tasks');
});
});
当我运行此测试时,我收到此错误。如果我使用rootScope,我会得到同样的错误。$ apply()代替:
Error: Unexpected request: GET views/AuthView.html
No more request expected
当我拿出范围。$ apply()时,我收到此错误:
Expected spy url to have been called with [ '/tasks' ] but it was never called.
有关如何解决此问题的任何想法?这是我第一次单独测试一个承诺,我可能在概念上误解了如何做到这一点。
提前感谢您的帮助。
答案 0 :(得分:4)
当你使用AngularJS进行单元测试时,你实际上是使用来自ngMock
模块的模拟服务而不是真实服务。在这种情况下,您的错误可能来自$httpBackend
mock,如果您希望测试单位使用$http
发送实际请求,则必须首先“准备”。 (我没有在任何地方看到你的代码使用$http
,所以我猜你有一部分你没有在这里展示。)
基本上,您需要告诉$httpBackend
模拟您使用$httpBackend.when
期望特定请求(或者您可以使用$httpBackend.expect
,如果您希望模拟检查请求真的被调用了)应该返回什么样的响应。
因此,您的错误与承诺无关,也与连接到服务器的测试单元有关。
答案 1 :(得分:1)
在问到这个问题差不多两年之后,也许是一个有争议的问题,但是接受的答案并不完全正确。问题是你没有正确地嘲笑AuthenticationService
。您应该完全模拟服务的功能,仅测试 从控制器调用服务,并保存$httpBackend
内容以测试服务本身。你要做的是注入假AuthenticationService
(以及$q
,$location
和$route
等Angular服务的模拟实例,如果我们追随最好的实践)到beforeEach
,然后在$controller
定义中声明它。您还可以在beforeEach
区块中包含您的间谍。
describe('AuthCtrl', function(){
var $q;
var $location;
var $controller;
var AuthenticationService;
var $route;
var deferred;
beforeEach(inject(function( _$controller_, $rootScope, _AuthenticationService_, _$q_, _$location_, _$route_){
scope = $rootScope.$new();
AuthenticationService = _AuthenticationService_;
$location = _$location_;
$route = _$route_;
$controller = _$controller_;
$q = _$q_;
deferred = $q.defer();
spyOn(AuthenticationService, 'login').andReturnValue(deferred.promise);
spyOn($location, 'url');
AuthCtrl = $controller('AuthCtrl', {
scope: scope,
AuthenticationService: AuthenticationService,
$route: $route,
$location: $location
};
}));
it('should redirect the user on login', function(){
deferred.resolve();
scope.$apply();
expect($location.url).toHaveBeenCalledWith('/tasks');
});
});
请注意,如果您以这种方式定义变量,那么将控制器定义作为beforeEach
块中的最后一项非常重要,否则将无法正确实例化模拟并实际调用服务方法,不管是间谍。