我的测试是:
(function() {
describe('Login Controller', function() {
beforeEach(module('myApp'));
beforeEach(inject(function($injector) {
var $controller, $httpBackend, $q, userServiceMock;
self.$rootScope = $injector.get('$rootScope');
self.$scope = $rootScope.$new();
self.$state = $injector.get('$state');
$q = $injector.get('$q');
$httpBackend = $injector.get('$httpBackend');
$controller = $injector.get('$controller');
userServiceMock = {
login: function(auth) {
self.deferred = $q.defer();
console.log("HERE!");
return self.deferred.promise;
}
};
self.createController = function() {
return $controller('LoginController', {
'$scope': self.$scope,
'$rootScope': self.$rootScope,
'$state': self.$state,
'userService': userServiceMock
});
};
return $httpBackend.whenPOST('http://localhost:9001/api/v1/session/check').respond({
authenticated: true
});
}));
it('should set the page title to "Login"', function() {
self.createController();
$scope.init();
expect($rootScope.pageTitle).toBe('Login');
return expect($scope.auth).toEqual({});
});
return it('should properly authenticate a user', function() {
self.createController();
$scope.init();
$scope.auth = {
username: 'test@test.com',
password: 'mypassword'
};
$scope.login();
self.deferred.resolve({
authenticated: true
});
$scope.$root.$digest();
console.log($state.current.name);
expect($state.current.name).toBe('dashboard');
});
});
})();
我认为很简单?我的控制器是:
myApp.controller('LoginController', [
'$scope', '$rootScope', '$state', 'userService', function($scope, $rootScope, $state, userService) {
$scope.init = function() {
$rootScope.pageTitle = 'Login';
return $scope.auth = {};
};
$scope.login = function() {
return userService.login($scope.auth).then(function(response) {
if (response.authenticated === true) {
return $state.transitionTo('dashboard');
} else {
return console.log('bad password man');
}
});
};
return $scope.init();
}
]);
我在测试中得到的是:
NFO [karma]: Karma v0.12.16 server started at http://localhost:9876/
INFO [launcher]: Starting browser PhantomJS
INFO [PhantomJS 1.9.7 (Mac OS X)]: Connected on socket TRjrAWL8PHdQ-bKqkyzK with id 61166122
LOG: 'HERE!'
PhantomJS 1.9.7 (Mac OS X) - Login Controller:
PhantomJS 1.9.7 (Mac OS X) should set the page title to "Login" PASSED
PhantomJS 1.9.7 (Mac OS X) should properly authenticate a user FAILED
PhantomJS 1.9.7 (Mac OS X): Executed 2 of 2 (1 FAILED) (0.02 secs / 0.018 secs)
PhantomJS 1.9.7 (Mac OS X)
PhantomJS 1.9.7 (Mac OS X) Login Controller should properly authenticate a user FAILED
Error: Unexpected request: GET templates/dashboard.html
No more request expected
发生这种情况的原因是状态转换为dashboard
,然后尝试加载dashboard.html
模板。我不一定想要/需要测试它。我只需要测试它是否达到了正确的状态。如何正确设置?
我使用Angular UI Router代替内置$routeProvider
答案 0 :(得分:6)
简短的回答是你需要告诉$ httpBackend来期待这个请求。如果任何请求被发送并且没有被$ httpBackend捕获,Karma将抛出错误,因为它们都应该在单元测试中被模拟。它可能看起来像......
$httpBackend.expectGET('/templates/dashboard.html');
$scope.login();
$httpBackend.flush();
我说这只是简短的答案,因为这里有一些事情,如果不提他们就会有点不利。首先,您使用$ httpBackend来捕获身份验证POST请求,但是您实际上并未发送请求,因为您正在模拟userService。你不需要同时做这两件事。您可以删除此代码......
return $httpBackend.whenPOST('http://localhost:9001/api/v1/session/check').respond({
authenticated: true
});
因为该请求实际上不会被您的模拟用户服务发送。该代码可能在您的userService的单元测试中很有用。
我还会使用beforeEach / afterEach挂钩。当你可以在beforeEach函数中调用一次并且它将在每次测试之前运行时,不需要在所有测试中调用self.createController。如果你不小心,这可能会变得混乱。
最后,测试视图更改并设置新页面标题正在推动单元测试的限制。理想情况下,您可以在集成测试中测试路由和页面更改(如果有的话)。你在第二次测试中唯一真正测试的是......
$state.transitionTo('dashboard')
做了它应该做的事情(将当前的$状态更新为'仪表板')。你应该相信这是有效的。 UI路由器有自己的测试证明这是有效的。我会努力使您的单元测试尽可能小,并且仅隔离测试应用程序各个组件的特定行为。所以不要问......
"当我调用$ scope.login()时,是否会调用userService的登录函数并发送POST请求并更改状态?"
我只想问......
"当我调用$ scope.login()时,是否会调用userService的登录函数?"
其余部分不是您的控制器的责任。是否发送请求是userService的责任(并且应该在那里进行测试)以及当你调用$ state.transitionTo时状态是否发生变化是ui-router的范围并且已经在那里进行了测试。保留跨越多个组件的高级测试以进行集成测试。
答案 1 :(得分:4)
您可以模拟模板文件,从而避免$templateCache
:
$templateCache = $injector.get('$templateCache');
$templateCache.put('templates/dashboard.html','<div>blank or whatever</div>');
......另一种方法是使用$httpBackend
...
顺便说一下,您可以随时将注射剂添加到$injector
功能中,而不是一直使用inject
......
beforeEach(inject(function($rootScope, $state, $httpBackend, $templateCache) {
我经常使用下划线语法来获得额外的灵活性:
beforeEach(inject(function(_$rootScope_, _$state_, _$httpBackend_, _$templateCache_) {