在AngularJS单元/集成测试中向HTTP服务器发出实际请求 [英] Making real requests to HTTP server in AngularJS unit/integration tests
问题描述
在Angular 1.x单元/集成测试中发出未使用 $ httpBackend.when
进行模拟的请求会导致错误:
Making a request that wasn't mocked with $httpBackend.when
in Angular 1.x unit/integration test results in an error:
错误:意外请求:GET / real-request
Error: Unexpected request: GET /real-request
是吗可以使用 ngMock
和Karma + Jasmine测试平台进行真正的HTTP请求?这样做的好习惯是什么?
Is it possible to make real HTTP requests with ngMock
and Karma+Jasmine test rig? What is a good practice to do that?
推荐答案
AngularJS是一个自以为是的框架,它对单元测试中HTTP请求的看法是所有这些都应该被模拟。
AngularJS is opinionated framework, and its opinion on HTTP requests in unit tests is that all of them should be mocked.
由于两个原因,不建议在单元测试中进行真正的HTTP请求。单元测试应该是隔离的并且速度很快。发出实际请求会使测试异步,从而显着降低测试运行速度。提出真正的请求会打破隔离,如果测试通过,则取决于测试单元和后端。
It is not advisable to do real HTTP requests in unit tests for two reasons. Unit tests are supposed to be isolated and be fast. Making a real request makes a test asynchronous, which slows down test runs significantly. Making a real request breaks the isolation, the fact if a test passes depends on both tested unit and a backend.
AngularJS ngMock
模块(它由angular-mocks.js在单元测试中自动加载)。开发人员几乎不会使用Angular 异步 Jasmine单元测试,因为没有必要这样做。
This was taken into consideration when AngularJS ngMock
module was designed (it is loaded automatically in unit tests by angular-mocks.js). The developer will hardly ever do asynchronous Jasmine unit tests with Angular, because there's no need to do that.
集成测试有所不同。它们可能不像E2E测试那样广泛(通常由Protractor运行)并测试多个单元如何协同工作,这可能包括后端(HTTP服务器)。所以最终仍然使用了Karma和Jasmine,但是测试可能更慢并且异步并且执行真正的HTTP请求。
Integration tests differ. They may be not as broad as E2E tests (which are often run by Protractor) and test how several units work together, this may include a backend (HTTP server). So in the end Karma and Jasmine are still used, but the tests may be slower and asynchronous and do real HTTP requests.
这是 ngMockE2E
模块(通常在E2E测试中使用)启动。它与angular-mocks.js一起包含在 ngMock
中,但默认情况下不加载。
This is where ngMockE2E
module (usually used in E2E tests) kicks in. It is included in angular-mocks.js alongside with ngMock
but isn't loaded by default.
ngMockE2E是一个AngularJS模块,它包含适用于端到端测试的模拟。目前该模块中只有一个模拟 - e2e $ httpBackend mock。
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各不相同。它不应该使用 flush
和 extend
方法。 $ rootScope。$ digest()
如果有 $ q
应该执行的承诺链,则可以使用。
ngMockE2E
contains different $httpBackend
implementation which can be used for the purpose. Its API varies. It isn't supposed to use flush
and extend
methods. $rootScope.$digest()
may be used if there are $q
promise chains that should be executed.
ngMockE2E
无法正常使用,因为 module
和 inject
时。可以使用用于集成测试的辅助模块:
ngMockE2E
won't work out of the box properly because of the the adjustments that are being made to Angular services by ngMock
when its helper functions module
and inject
are used. A helper module for intergration tests can be used instead:
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请求可以使测试更容易,尽管最佳做法是明确枚举真实和模拟请求。
Additionally, a recipe for whitelisted real HTTP requests can be used to make the testing easier, although the best practice is to enumerate real and mocked requests explicitly.
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
兼容。在单元测试中永远不要做真正的请求,这会导致测试缓慢且无用。
TL;DR: Use $httpBackend
from ngMockE2E
in integration tests for real requests, this requires some extra work to make it compatible with ngMock
. Never do real requests in unit tests, this results in slow and trashy tests.
这篇关于在AngularJS单元/集成测试中向HTTP服务器发出实际请求的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!