【问题标题】:Unit testing the AngularJS $window service对 AngularJS $window 服务进行单元测试
【发布时间】:2014-02-08 11:48:17
【问题描述】:

我想对以下 AngularJs 服务进行单元测试:

.factory('httpResponseInterceptor', ['$q', '$location', '$window', 'CONTEXT_PATH', function($q, $location, $window, contextPath){
     return {
         response : function (response) {
             //Will only be called for HTTP up to 300
             return response;
         },
         responseError: function (rejection) {
             if(rejection.status === 405 || rejection.status === 401) {
                 $window.location.href = contextPath + '/signin';
             }
             return $q.reject(rejection);
         }
     };
}]);

我已尝试使用以下套件:

describe('Controllers', function () {
    var $scope, ctrl;
    beforeEach(module('curriculumModule'));
    beforeEach(module('curriculumControllerModule'));
    beforeEach(module('curriculumServiceModule'));
    beforeEach(module(function($provide) {
       $provide.constant('CONTEXT_PATH', 'bignibou'); // override contextPath here
    }));
    describe('CreateCurriculumCtrl', function () {
        var mockBackend, location, _window;
        beforeEach(inject(function ($rootScope, $controller, $httpBackend, $location, $window) {
            mockBackend = $httpBackend;
            location = $location;
            _window = $window;
            $scope = $rootScope.$new();
            ctrl = $controller('CreateCurriculumCtrl', {
                $scope: $scope
            });
        }));

        it('should redirect to /signin if 401 or 405', function () {
            mockBackend.whenGET('bignibou/utils/findLanguagesByLanguageStartingWith.json?language=fran').respond([{"description":"Français","id":46,"version":0}]);
            mockBackend.whenPOST('bignibou/curriculum/new').respond(function(method, url, data, headers){
                return [401];
            });
            $scope.saveCurriculum();
            mockBackend.flush();
            expect(_window.location.href).toEqual("/bignibou/signin");
        });


    });
});

但是,它失败并显示以下错误消息:

PhantomJS 1.9.2 (Linux) Controllers CreateCurriculumCtrl should redirect to /signin if 401 or 405 FAILED
    Expected 'http://localhost:9876/context.html' to equal '/bignibou/signin'.
PhantomJS 1.9.2 (Linux) ERROR
    Some of your tests did a full page reload!

我不确定出了什么问题以及为什么。有人可以帮忙吗?

我只想确保$window.location.href 等于'/bignibou/signin'

编辑 1

我设法让它工作如下(感谢“dskh”):

 beforeEach(module('config', function($provide){
      $provide.value('$window', {location:{href:'dummy'}});
 }));

【问题讨论】:

    标签: angularjs


    【解决方案1】:

    您可以在加载模块时注入存根依赖项:

    angular.mock.module('curriculumModule', function($provide){
                $provide.value('$window', {location:{href:'dummy'}});
            });
    

    【讨论】:

    • 谢谢。我应该使用什么作为 $window 而不是 {} 的值?
    • 随心所欲:)。您可能只想检查 $window.location 是否未定义,以便稍后在测试将其设置为值时检查它
    • 我也在使用这种方法,但是它破坏了我引用 $location 的测试。你如何模拟 $window 并让 $location 引用它?
    • 如果你也存根 $location,它将不再引用 $window。
    【解决方案2】:

    为了让这个对我有用,我必须做一个小的调整。它会出错并说:

    TypeError: 'undefined' is not an object (evaluating '$window.navigator.userAgent')

    所以我添加了navigator.userAgent 对象,让它为我工作。

    $provide.value('$window', {
      location:{
        href:'dummy'
      },
      navigator:{
        userAgent:{}
      }
    });
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,并在我的解决方案中更进一步。我不只是想要一个模拟,我想用 Jasmine 间谍替换 $window.location.href,以便更好地跟踪对其所做的更改。所以,我从 apsiller's example for spying on getters/setters 那里学到了东西,在创建了我的模拟之后,我能够监视我想要的属性。

      首先,这是一个展示我如何模拟 $window 的套件,并通过测试证明间谍按预期工作:

      describe("The Thing", function() {
          var $window;
      
          beforeEach(function() {
              module("app", function ($provide) {
                  $provide.value("$window", {
                      //this creates a copy that we can edit later
                      location: angular.extend({}, window.location)
                  });
              });
      
              inject(function (_$window_) {
                  $window = _$window_;
              });
          });
      
          it("should track calls to $window.location.href", function() {
              var hrefSpy = spyOnProperty($window.location, 'href', 'set');
      
              console.log($window.location.href);
              $window.location.href = "https://www.google.com/";
              console.log($window.location.href);
      
              expect(hrefSpy).toHaveBeenCalled();
              expect(hrefSpy).toHaveBeenCalledWith("https://www.google.com/");
          });
      });
      

      正如您在上面看到的,间谍是通过调用以下函数生成的:(它适用于getset

      function spyOnProperty(obj, propertyName, accessType) {
          var desc = Object.getOwnPropertyDescriptor(obj, propertyName);
      
          if (desc.hasOwnProperty("value")) {
              //property is a value, not a getter/setter - convert it
              var value = desc.value;
      
              desc = {
                  get: function() { return value; },
                  set: function(input) { value = input; }
              }
          }
      
          var spy = jasmine.createSpy(propertyName, desc[accessType]).and.callThrough();
      
          desc[accessType] = spy;
          Object.defineProperty(obj, propertyName, desc);
      
          return spy;
      }
      

      最后,here's a fiddle demonstrating this in action。我已经针对 Angular 1.4、Jasmine 2.3 和 2.4 进行了测试。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-05-10
      • 1970-01-01
      • 1970-01-01
      • 2016-05-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多