4

製作,這不是在角1.x的單元/集成測試結果的誤差$httpBackend.when嘲笑的請求:製作到HTTP服務器請求實際在AngularJS單元/集成測試

Error: Unexpected request: GET /real-request

是否有可能使用ngMock和Karma + Jasmine測試裝備製作真正的HTTP請求?什麼是一個很好的做法呢?

+0

請解釋一下在你的情況下什麼是「真正的」HTTP請求? – lin

+0

真正的請求是調用window.XMLHttpRequest並向HTTP服務器發出請求的請求。這在Angular單元測試中不被設計所允許。 – estus

+0

@lin這是自我回答,順便說一句。我認爲這是一個臭名昭着的問題。 – estus

回答

5

AngularJS是一個自發的框架,它在單元測試中對HTTP請求的看法是它們都應該被嘲笑。

出於兩個原因,不建議在單元測試中執行真正的HTTP請求。單元測試應該是孤立的,速度很快。提出真正的請求會使測試異步,從而顯着降低測試運行速度。提出真正的請求會打破隔離,測試通過的事實取決於被測試單元和後端。

設計了AngularJS ngMock模塊(在angular-mocks.js的單元測試中自動加載)時考慮了這一點。開發人員幾乎不會這樣做asynchronous用Angular進行Jasmine單元測試,因爲沒有必要那麼做。

集成測試不同。它們可能不像E2E測試那樣廣泛(通常由Protractor運行),並測試幾個單元如何協同工作,這可能包括後端(HTTP服務器)。所以最終Karma和Jasmine仍在使用,但測試可能會比較慢並且異步,並且會執行真正的HTTP請求。

這是ngMockE2E模塊(通常用於E2E測試)踢入。它包含在angular-mocks.js中,並與ngMock並列,但默認情況下未加載。

The ngMockE2E is an AngularJS module which contains mocks suitable for end-to-end testing. Currently there is only one mock present in this module - the e2e $httpBackend mock.

ngMockE2E包含不同$httpBackend實現可用於該目的。其API不盡相同。它不應該使用flushextend方法。如果有$q承諾鏈應該執行,則可以使用$rootScope.$digest()

ngMockE2E將開箱即用,因爲正在對角服務所做的調整適當的通過ngMock當它的輔助功能moduleinject使用不起作用。對於一體化測試一個輔助模塊,可以用來代替:

angular.module('ngMockI9n', []).config(function ($provide) { 
    // hack to restore original implementations which were overridden by ngMock 
    angular.injector(['ng', function ($httpBackendProvider, $browserProvider) { 
    $provide.provider('$httpBackend', $httpBackendProvider); 
    $provide.provider('$browserI9n', $browserProvider); 
    }]); 

    // make ngMockE2E $httpBackend use original $browser 
    var httpBackendI9nDecorator = angular.mock.e2e.$httpBackendDecorator 
    .map(function (dep) { 
    return (dep === '$browser') ? '$browserI9n' : dep; 
    }); 
    $provide.decorator('$httpBackend', httpBackendI9nDecorator); 
}); 

此外,對於whitelisted real HTTP requests配方可以用來使測試更容易,但最好的做法是明確地列舉真實和嘲笑的請求。

beforeEach(module('app')); 
beforeEach(module('ngMockI9n')); 

beforeEach(inject(function ($httpBackend) { 
    $httpBackend.when('GET', '/mocked-request').respond(200, {}); 

    // all other requests will be automatically whitelisted and treated as real 
    // so make sure that mocked requests are mocked above this point 
    angular.forEach(['GET', 'DELETE', 'JSONP', 'HEAD', 'PUT', 'POST', 'PATCH'], 
    function (method) { 
    $httpBackend.when(method).passThrough(); 
    }); 
})); 


it('does real async request', function (done) { 
    // async tests need extra `done` param 
    inject(function() { 
    $http.get('real-request').then(function (response) { 
     expect(response.data).toEqual(...); 
    }) 
    .then(done, done.fail); 

    $rootScope.$digest(); 
    }); 
}); 

it('does mocked sync request', function (done) { 
    // tests with mocked requests are async, too 
    inject(function() { 
    $http.get('mocked-request').then(function (response) { 
     expect(response.data).toEqual(...); 
    }) 
    .then(done, done.fail); 

    $rootScope.$digest(); 
    }); 
}); 

TL; DR:使用$httpBackend在集成測試的實際要求ngMockE2E,這需要一些額外的工作,使其與ngMock兼容。從不在單元測試中做出真正的請求,這會導致緩慢且垃圾的測試。

相關問題