如何在 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?的主要内容,如果未能解决你的问题,请参考以下文章

在 AngularJS 单元测试中模拟 $modal

在 Jasmine 单元测试中模拟 AngularJS 模块依赖项

在 AngularJS 单元测试中模拟模块依赖

在AngularJS单元测试中模拟模块依赖性

基于karma和jasmine的Angularjs 单元测试

Angularjs 基于karma和jasmine的单元测试