2014-02-08 45 views
13

我想單元測試以下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'}}); 
})); 

回答

17

當您在模塊中的負載,可以注入存根依賴關係:

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

謝謝。我應該使用'$ window'而不是'{}'作爲值嗎? – balteo

+1

任何你想要的:)。你可能只是想檢查$ window.location的是不是不確定的,這樣以後可以檢查它,當你測試設置爲一個值 – Dan

+0

我使用這個方法很好,但它打破了我的測試中被引用$位置。你如何模擬$ window並獲取$ location來引用它? –

2

爲了得到這個工作對我來說,我不得不做出微調。它會出錯誤,並說:

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

所以我加入了navigator.userAgent對象得到它爲我工作。

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

我面臨同樣的問題,並在我的解決方案中更進一步。我不只是想要一個模擬,我想用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進行了測試。

+0

茉莉2.6(也許更早,不知道)的'spyOnProperty'已經合併到核心,而不需要由用戶來實現:https://jasmine.github.io/api/edge/global.html# spyOnProperty –