【问题标题】:How to mock $window.location.replace in AngularJS unit test?如何在 AngularJS 单元测试中模拟 $window.location.replace?
【发布时间】: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 的解决方案。

标签: javascript unit-testing angularjs mocking angularjs-service


【解决方案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();

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-02-08
      • 2017-09-25
      • 2014-03-23
      • 1970-01-01
      • 2017-06-14
      • 2013-07-07
      • 1970-01-01
      • 2013-10-11
      相关资源
      最近更新 更多