如何通过 jasmine 在 angular 测试中避免 location.reload() 并对其进行测试
How to avoid location.reload() in angular tests via jasmine, and test it
在我的一个控制器中,我有一个 location.reload()
(对于那些想知道为什么的人,我的登录页面在用户登录之前不会加载整个应用程序以降低服务器上的门槛)
当我测试控制器时(使用 jasmine html 作为 ui),它实际上会在无限循环中重新加载测试。
我的问题是:
如何避免 location.reload() 加载测试本身,甚至如何检查它是否已被调用?我能以某种方式嘲笑它吗?
控制器代码如下:
(function() {
'use strict';
angular
.module('app.ctrls')
.controller('LoginController', LoginController);
LoginController.$inject = [ '$scope', 'AuthService' ];
function LoginController($scope, AuthService)
{
console.log('LoginController');
$scope.credantials = {
email: '',
password: '',
remember: false
}
$scope.login = function(credantials)
{
AuthService.login(credantials.email, credantials.password, credantials.remember || false).success(function(response) {
console.log(response);
if (response.email !== null)
{
$scope.logged = true;
console.log('login successful');
location.reload();
}
else
{
$scope.logged = false;
$scope.error = true;
}
}).error(function() {
console.log('failed to login');
});
};
}
})();
规格如下:
describe("successfully logged in", function() {
it("should reload the page", function() {
this.scope.credantials = { email: 'test@mail.com', password: '123', remember: false };
this.$httpBackend.expectPOST('/auth/login', this.scope.credantials).respond({first_name: "Bakayaru", email: "test@mail.com"});
this.$httpBackend.whenGET('/tpls/home.html').respond(200);
this.scope.login(this.scope.credantials);
this.$httpBackend.flush();
expect(this.scope).toBeDefined();
expect(this.scope.logged).toBeTruthy();
/* -------> TEST that location.reload() has been called HERE <--------- */
});
});
我不确定你所说的 "not loading itself" 是什么意思,但无论如何都可以在测试中模拟 .reload() 。我首先在我的应用程序中使用 Angular 服务 $window.location 而不是我的应用程序中的本地 javascript 位置,就像这样在注入 $window 作为对我的控制器的依赖之后:
$window.location.reload();
然后在规格文件中我有以下内容(一个示例测试用例使用模拟重载):
describe('Controller: MyCtrl', function () {
// load the controller's module
beforeEach(module('myApp'));
var MyCtrl,
scope,
mockBackend,
window;
beforeEach(function(){
inject(function(_$httpBackend_, $controller, $rootScope, $window) {
scope = $rootScope.$new();
mockBackend = _$httpBackend_;
window = $window;
spyOn(window.location, 'reload');
myCtrl = $controller('MyCtrl', {
$scope: scope,
$window: window
});
});
});
it('logout should fail if $window.reload() is called on unsuccessful logout call', function(){
scope.logout();
mockBackend.expectGET('/rest/session/logout').respond(404);
mockBackend.flush();
expect(window.location.reload.calls.count()).toEqual(0);
mockBackend.verifyNoOutstandingExpectation();
mockBackend.verifyNoOutstandingRequest();
});
});
这与您的规范文件的结构有点不同,但我希望这个示例阐明了如何模拟 $window 对象并对其使用间谍。
我意识到,就像 PHP 中的单元测试一样,当代码有 file_get_content
时,你必须自己模拟它,而不是测试它。
location.reload
是一个浏览器组件,因此必须模拟我的代码才能测试。
因此我创建了一个 BrowserService
:
(function() {
'use strict';
angular
.module('rushcore.services')
.factory('BrowserService', BrowserService);
function BrowserService()
{
console.log('BrowserService');
return {
reload: function() {
location.reload();
}
};
}
})();
在测试中我模拟并注入了它。
var BrowserServiceMock = { reload: function(){ console.log("reloaded"); } };
然后我偷看了它:
var reload = spyOn(BrowserServiceMock, 'reload');
最后确定它被称为:
expect(reload).toHaveBeenCalled();
你可以像这样在你的测试中模拟 $window:
describe('service', function () {
var $window;
beforeEach(module('core', function ($provide) {
$window = {
location:{
reload: jasmine.createSpy()
}
};
$provide.value('$window', $window);
}));
});
这样它将 "override" $window 的默认行为。
在我的一个控制器中,我有一个 location.reload()
(对于那些想知道为什么的人,我的登录页面在用户登录之前不会加载整个应用程序以降低服务器上的门槛)
当我测试控制器时(使用 jasmine html 作为 ui),它实际上会在无限循环中重新加载测试。
我的问题是:
如何避免 location.reload() 加载测试本身,甚至如何检查它是否已被调用?我能以某种方式嘲笑它吗?
控制器代码如下:
(function() {
'use strict';
angular
.module('app.ctrls')
.controller('LoginController', LoginController);
LoginController.$inject = [ '$scope', 'AuthService' ];
function LoginController($scope, AuthService)
{
console.log('LoginController');
$scope.credantials = {
email: '',
password: '',
remember: false
}
$scope.login = function(credantials)
{
AuthService.login(credantials.email, credantials.password, credantials.remember || false).success(function(response) {
console.log(response);
if (response.email !== null)
{
$scope.logged = true;
console.log('login successful');
location.reload();
}
else
{
$scope.logged = false;
$scope.error = true;
}
}).error(function() {
console.log('failed to login');
});
};
}
})();
规格如下:
describe("successfully logged in", function() {
it("should reload the page", function() {
this.scope.credantials = { email: 'test@mail.com', password: '123', remember: false };
this.$httpBackend.expectPOST('/auth/login', this.scope.credantials).respond({first_name: "Bakayaru", email: "test@mail.com"});
this.$httpBackend.whenGET('/tpls/home.html').respond(200);
this.scope.login(this.scope.credantials);
this.$httpBackend.flush();
expect(this.scope).toBeDefined();
expect(this.scope.logged).toBeTruthy();
/* -------> TEST that location.reload() has been called HERE <--------- */
});
});
我不确定你所说的 "not loading itself" 是什么意思,但无论如何都可以在测试中模拟 .reload() 。我首先在我的应用程序中使用 Angular 服务 $window.location 而不是我的应用程序中的本地 javascript 位置,就像这样在注入 $window 作为对我的控制器的依赖之后:
$window.location.reload();
然后在规格文件中我有以下内容(一个示例测试用例使用模拟重载):
describe('Controller: MyCtrl', function () {
// load the controller's module
beforeEach(module('myApp'));
var MyCtrl,
scope,
mockBackend,
window;
beforeEach(function(){
inject(function(_$httpBackend_, $controller, $rootScope, $window) {
scope = $rootScope.$new();
mockBackend = _$httpBackend_;
window = $window;
spyOn(window.location, 'reload');
myCtrl = $controller('MyCtrl', {
$scope: scope,
$window: window
});
});
});
it('logout should fail if $window.reload() is called on unsuccessful logout call', function(){
scope.logout();
mockBackend.expectGET('/rest/session/logout').respond(404);
mockBackend.flush();
expect(window.location.reload.calls.count()).toEqual(0);
mockBackend.verifyNoOutstandingExpectation();
mockBackend.verifyNoOutstandingRequest();
});
});
这与您的规范文件的结构有点不同,但我希望这个示例阐明了如何模拟 $window 对象并对其使用间谍。
我意识到,就像 PHP 中的单元测试一样,当代码有 file_get_content
时,你必须自己模拟它,而不是测试它。
location.reload
是一个浏览器组件,因此必须模拟我的代码才能测试。
因此我创建了一个 BrowserService
:
(function() {
'use strict';
angular
.module('rushcore.services')
.factory('BrowserService', BrowserService);
function BrowserService()
{
console.log('BrowserService');
return {
reload: function() {
location.reload();
}
};
}
})();
在测试中我模拟并注入了它。
var BrowserServiceMock = { reload: function(){ console.log("reloaded"); } };
然后我偷看了它:
var reload = spyOn(BrowserServiceMock, 'reload');
最后确定它被称为:
expect(reload).toHaveBeenCalled();
你可以像这样在你的测试中模拟 $window:
describe('service', function () {
var $window;
beforeEach(module('core', function ($provide) {
$window = {
location:{
reload: jasmine.createSpy()
}
};
$provide.value('$window', $window);
}));
});
这样它将 "override" $window 的默认行为。