发出未$httpBackend.when在Angular 1.x单元/集成测试中模拟的请求会导致错误:
$httpBackend.when
错误:意外的请求:GET / real-request
是否可以使用ngMockKarma + Jasmine测试设备发出真正的HTTP请求?有什么好的做法?
ngMock
AngularJS是有思想的框架,它对单元测试中的HTTP请求的看法是所有这些都应该被模拟。
不建议在单元测试中执行真实的HTTP请求,这有两个原因。单元测试应该是隔离的并且是快速的。发出真实请求会使测试异步,这会大大降低测试运行速度。发出真实请求会破坏隔离,测试通过的事实取决于被测单元和后端。
ngMock设计AngularJS 模块时已考虑到了这一点(它由angular- mocks.js在单元测试中自动加载)。开发人员几乎不会使用Angular 进行异步 Jasmine单元测试,因为不需要这样做。
集成测试有所不同。它们可能不像E2E测试(通常由量角器运行)那样广泛,并测试多个单元如何协同工作,其中可能包括后端(HTTP服务器)。因此最后仍然使用Karma和Jasmine,但是测试可能会比较慢且异步,并且会执行真正的HTTP请求。
这是ngMockE2E模块(通常用于E2E测试中)插入的位置。它与angular-mocks.js一起包含,ngMock但默认情况下未加载。
ngMockE2E
ngMockE2E是一个AngularJS模块,其中包含适用于端到端测试的模拟。当前,此模块中仅提供一个模拟-e2e $ httpBackend模拟。
ngMockE2E包含$httpBackend可以用于此目的的不同实现。其API有所不同。它不应该使用flush和extend方法。$rootScope.$digest()如果有$q应执行的承诺链,则可以使用。
$httpBackend
flush
extend
$rootScope.$digest()
$q
ngMockE2E由于Angular服务的ngMock助手功能module和inject使用时对Angular服务所做的调整,因此无法正常使用。可以使用用于集成测试的帮助程序模块:
module
inject
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); });
此外,用于白名单的真实HTTP请求的配方可用于简化测试,尽管最佳实践是显式枚举真实和模拟的请求。
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。永远不要在单元测试中执行真正的请求,这会导致测试缓慢而浪费资源。