如何在不更改所有单元测试的情况下避免 "Unexpected request: GET" 在 app.run 周期内进行的调用
How to avoid "Unexpected request: GET" for a call made during app.run cycle without changing all my unit tests
我的 Angular 应用有一个获取登录用户信息的工厂:
.factory('UserInfo', function ($http) {
return $http.get('/api/v1/whoami');
}
应用程序完成引导后立即注入此工厂。
在添加这个工厂之前,我所有的单元测试都通过了,但是在添加它之后,所有使用 $httpBackend
的单元测试都失败了,并显示错误消息:
Error: Unexpected request: GET /api/v1/whoami
Expected GET /api/v1/foobar
我可以在评论下方添加两行:
beforeEach(inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
//these 2 lines fix all unit tests for this "describe"
$httpBackend.expectGET('/api/v1/whoami').respond({foo: 'bar'});
$httpBackend.flush();
}));
所有使用 $httpBackend
使它们再次通过的单元测试,或者为我的 UserInfo
工厂编写模拟,但我也希望能够测试工厂。
有没有办法避免这个错误,而不必在每个使用 $httpBackend
的 describe
块上写这两行?
很多时候会有app需要的东西才能得到运行。比如用户认证、翻译等
对于这些情况,我通常在每个规范的 beforeEach 中添加这些期望。
describe('Directive test', function() {
var $httpBackend;
beforeEach(inject(function($injector) {
// inject your stuff
$httpBackend = $injector.get('$httpbackend')
// set up app specific expectations
$httpBackend.whenGET('/api/account').respond({});
}))
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('first test', function() {
// do your tests
})
})
我个人认为这种方法没有任何问题。围绕测试会有一些重复,但认为没关系。
有一次您需要承认,当测试开始并加载您的模块时,应用程序在技术上正在启动。这将启动您的一些应用程序代码。所以要么在测试中处理这些期望,要么改变你的应用程序。大多数时候在测试中处理这个问题会更好。
您肯定不想在每个单元测试中添加两行。如果 bootstrapping 逻辑发生变化(例如添加另一个 API 调用或清除缓存),您将不得不再次修改每个单元测试。
您可以将所有 bootstrap-support 代码(目前为 2 行)移动到 testUtils 服务中的 setup 函数中.然后,当 bootstrap 逻辑发生变化时,您将只需要更改一个函数。然而,你仍然会有脆弱的测试。红色测试应该表明被测试的单元出了问题。当 bootstrap 逻辑发生变化并且测试变红时,团队将需要争先恐后地找出错误的根本原因并对 testUtils.setup 进行必要的更新(这可能不仅仅是一个额外的 $httpBackend.whenGET)。
更好的解决方案是将所有组件(服务、指令、控制器、过滤器)移出主 (app) 模块。每个模块应包含单个组件或几个紧密耦合的组件。然后每个单元测试将只加载被测试的模块,绕过 bootstrap 代码。事实上,karma.conf.js根本不需要加载app.js。您也可以依靠这种方法将单元测试的速度提高一倍。
至于测试UserInfo,将其移动到自己的模块中并为其编写单元测试。
我刚刚带领一个团队进行了上述更改。工作时间不到一天,我们的单元测试现在速度更快,也更健壮。
有关模块化和单元测试的讨论,请参阅 Angular Developer Guide。
在这种情况下,您的选择是:
- 如您所指
$httpBackend.expectGET(...).respond(...)
使用
- 在执行
UserInfo
的查找时使用 $provide
to pass a different implementation
现在您可能最终会在多个地方重复此操作(取决于您的项目结构)。如果是这种情况,只需提取代码块,将其放入单独的 JS 文件中,然后将该文件包含在您的运行程序中(如果您使用 grunt-contrib-jasmine 之类的东西,请将所述文件添加到 vendor
列表中.)
我的 Angular 应用有一个获取登录用户信息的工厂:
.factory('UserInfo', function ($http) {
return $http.get('/api/v1/whoami');
}
应用程序完成引导后立即注入此工厂。
在添加这个工厂之前,我所有的单元测试都通过了,但是在添加它之后,所有使用 $httpBackend
的单元测试都失败了,并显示错误消息:
Error: Unexpected request: GET /api/v1/whoami
Expected GET /api/v1/foobar
我可以在评论下方添加两行:
beforeEach(inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
//these 2 lines fix all unit tests for this "describe"
$httpBackend.expectGET('/api/v1/whoami').respond({foo: 'bar'});
$httpBackend.flush();
}));
所有使用 $httpBackend
使它们再次通过的单元测试,或者为我的 UserInfo
工厂编写模拟,但我也希望能够测试工厂。
有没有办法避免这个错误,而不必在每个使用 $httpBackend
的 describe
块上写这两行?
很多时候会有app需要的东西才能得到运行。比如用户认证、翻译等
对于这些情况,我通常在每个规范的 beforeEach 中添加这些期望。
describe('Directive test', function() {
var $httpBackend;
beforeEach(inject(function($injector) {
// inject your stuff
$httpBackend = $injector.get('$httpbackend')
// set up app specific expectations
$httpBackend.whenGET('/api/account').respond({});
}))
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('first test', function() {
// do your tests
})
})
我个人认为这种方法没有任何问题。围绕测试会有一些重复,但认为没关系。
有一次您需要承认,当测试开始并加载您的模块时,应用程序在技术上正在启动。这将启动您的一些应用程序代码。所以要么在测试中处理这些期望,要么改变你的应用程序。大多数时候在测试中处理这个问题会更好。
您肯定不想在每个单元测试中添加两行。如果 bootstrapping 逻辑发生变化(例如添加另一个 API 调用或清除缓存),您将不得不再次修改每个单元测试。
您可以将所有 bootstrap-support 代码(目前为 2 行)移动到 testUtils 服务中的 setup 函数中.然后,当 bootstrap 逻辑发生变化时,您将只需要更改一个函数。然而,你仍然会有脆弱的测试。红色测试应该表明被测试的单元出了问题。当 bootstrap 逻辑发生变化并且测试变红时,团队将需要争先恐后地找出错误的根本原因并对 testUtils.setup 进行必要的更新(这可能不仅仅是一个额外的 $httpBackend.whenGET)。
更好的解决方案是将所有组件(服务、指令、控制器、过滤器)移出主 (app) 模块。每个模块应包含单个组件或几个紧密耦合的组件。然后每个单元测试将只加载被测试的模块,绕过 bootstrap 代码。事实上,karma.conf.js根本不需要加载app.js。您也可以依靠这种方法将单元测试的速度提高一倍。
至于测试UserInfo,将其移动到自己的模块中并为其编写单元测试。
我刚刚带领一个团队进行了上述更改。工作时间不到一天,我们的单元测试现在速度更快,也更健壮。
有关模块化和单元测试的讨论,请参阅 Angular Developer Guide。
在这种情况下,您的选择是:
- 如您所指
$httpBackend.expectGET(...).respond(...)
使用 - 在执行
UserInfo
的查找时使用$provide
to pass a different implementation
现在您可能最终会在多个地方重复此操作(取决于您的项目结构)。如果是这种情况,只需提取代码块,将其放入单独的 JS 文件中,然后将该文件包含在您的运行程序中(如果您使用 grunt-contrib-jasmine 之类的东西,请将所述文件添加到 vendor
列表中.)