如何在 AngularJS 单元测试中模拟 $window.location.replace?
Posted
技术标签:
【中文标题】如何在 AngularJS 单元测试中模拟 $window.location.replace?【英文标题】:How to mock $window.location.replace in AngularJS unit test? 【发布时间】:2013-12-13 16:19:16 【问题描述】:我有以下服务:
angular.module("services")
.factory("whatever", function($window)
return
redirect: function()
$window.location.replace("http://www.whatever.com");
;
);
如何在单元测试中模拟$window
对象以防止在运行测试时重新加载页面?
我尝试过使用
spyOn($window.location, 'replace').andReturn(true);
,但它不起作用(仍然出现"Some of your tests did a full page reload!"
错误)并且
$provide.value('$window', location: replace: jasmine.createSpy())
,但我收到一个错误 (Error: [ng:areq] Argument 'fn' is not a function, got Object
),堆栈跟踪仅指向 Angular 自己的源,所以它不是很有帮助...
【问题讨论】:
我也遇到了同样的问题。你找到解决办法了吗? PaulL 提供的将$window.location
包装在单独的服务中的解决方案实际上工作得很好。还没有尝试过 LostInComputer 的解决方案。
【参考方案1】:
我认为您想要的是使用$location 服务,而不是调用$window.location
。这里还有一整页解释了这个特性:http://docs.angularjs.org/guide/dev_guide.services.$location。
使用它,在您的测试中使用 $location 服务的存根版本应该相当简单。
【讨论】:
谢谢,但根据文档:“当您需要更改 URL 并重新加载页面或导航到其他页面时,请使用较低级别的 API,$window.location.href。”这正是我想要的 - 重定向到外部 URL。 啊,我明白了。我不认为,这是可能的,但我唯一能想象到那将如何工作的事情就是用自己的对象存根 window.location 对象。【参考方案2】:在 Chrome 中(未在其他浏览器中测试),location.replace 是只读的,因此 spyOn 无法替换它。
$provide.value
应该可以工作。您的代码中一定有问题。
这是一个工作单元测试
describe('whatever', function()
var $window, whatever;
beforeEach(module('services'));
beforeEach(function()
$window = location: replace: jasmine.createSpy() ;
module(function($provide)
$provide.value('$window', $window);
);
inject(function($injector)
whatever = $injector.get('whatever');
);
);
it('replace redirects to http://www.whatever.com', function()
whatever.redirect();
expect($window.location.replace).toHaveBeenCalledWith('http://www.whatever.com');
);
);
【讨论】:
Angular 仍然将实际的窗口对象注入到服务中 这是一个working code,展示了我发布的答案。要运行,1. 你必须有 NodeJS 2. 调用 'npm install' 3. 调用 'grunt test' 来运行测试 抱歉!我发现我的测试代码中有一个错误。 我还必须提供一个scrollTo
spy,否则 angularjs 会抱怨:$window = location: replace: jasmine.createSpy() , scrollTo: jasmine.createSpy()
您使用的全局module
是什么?是angular.module
吗? angular.mock.module
?【参考方案3】:
我将提供另一种可能对您有用的方法。在对最终重定向用户的控制器“操作”进行单元测试时,我遇到了同样的问题(全页面加载,但到更大的网站/应用程序中的不同页面)。为了提供一些上下文,控制器触发 AJAX 请求,如果响应正常,它将通过 $window.location.replace() 将用户重定向到不同的页面:
$http.post('save', data)
.success(function(responseData, status, headers, config)
if(responseData.redirect)
$window.location.replace(responseData.redirect);
)
.error(function(responseData, status, headers, config)
console.error("ERROR while trying to create the Event!!");
);
对该控制器功能的测试导致相同的“您的一些测试完成了整页重新加载!”错误。所以我在控制器规范的 beforeEach() 函数中添加了以下内容,以模拟 $window 服务:
mockWindow = location: replace: function(url) console.log('redirecting to: ' + url); ;
eventCtrl = $controller('EventCtrl', $scope: scope, $window: mockWindow );
当然,这个解决方案阻止我(干净地)验证替换函数是用预期的参数调用的,但我现在并不真正关心这一点......希望这会有所帮助。
【讨论】:
这适用于控制器,但如果您想将 $window 注入服务,则会失败。【参考方案4】:我将采用更简单但可能不太优雅的解决方案。我正在为 $window.location 编写一个包装器,然后我可以对其进行模拟。将其与您的代码相关联,我将模拟whatever.redirect 函数,而不是模拟$window(我在这里假设您的真实函数更复杂)。
所以我最终会得到:
angular.module("services")
.factory("whatever", function($window)
return
do_stuff_that_redirects: function()
lots of code;
this.redirect("http://www.whatever.com");
maybe_more_code_maybe_not;
,
redirect: function(url)
$window.location.replace(url);
;
);
然后我可以直接模拟重定向方法,并且相信因为它只是一行代码,所以它不会真的出错。
spyOn(whatever, 'redirect').andCallFake(function());
expect(whatever.redirect).toHaveBeenCalledWith('http:/my.expected/url');
这对我的目的来说已经足够了,并且可以让我验证调用的 url。
【讨论】:
【参考方案5】:$location.path('/someNewPath');
$location.replace(); // 或者你可以将它们链接为: $location.path('/someNewPath').replace();
【讨论】:
以上是关于如何在 AngularJS 单元测试中模拟 $window.location.replace?的主要内容,如果未能解决你的问题,请参考以下文章
在 Jasmine 单元测试中模拟 AngularJS 模块依赖项