我的Angular应用程序有一个工厂,可以获取登录的用户信息:
.factory('UserInfo', function ($http) {
return $http.get('/api/v1/whoami');
}
应用程序完成引导后立即注入此工厂。
在添加此工厂之前,我的所有单元测试都已通过,但在添加之后,使用$httpBackend
的所有单元测试都打破了错误消息:
Error: Unexpected request: GET /api/v1/whoami
Expected GET /api/v1/foobar
我可以在评论下方添加2行:
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
工厂写一个模拟器,但我也希望能够测试工厂。
有没有办法避免此错误,而不必在使用describe
的每个$httpBackend
块上写下这两行?
答案 0 :(得分:4)
您绝对不希望为每个单元测试添加两行。如果引导逻辑发生更改(例如,添加了另一个API调用或清除了缓存),则必须再次修改每个单元测试。
您可以将所有引导支持代码(目前为2行)移至 testUtils 服务中的设置功能。然后,当引导逻辑发生更改时,您只需要更改一个函数。但是,你仍然会进行脆弱的测试。红色测试应该表明被测单元出了问题。当引导逻辑发生变化并且测试变为红色时,团队将需要争先恐后地找到错误的根本原因并对 testUtils.setup 进行必要的更新(这可能比仅仅更多额外的 $ httpBackend.whenGET )。
更好的解决方案是将所有组件(服务,指令,控制器,过滤器)移出主( app )模块。每个模块应包含单个组件或几个紧密耦合的组件。然后,每个单元测试将仅加载正在测试的模块,绕过引导代码。事实上, karma.conf.js 根本不需要加载 app.js 。您可以依靠这种方法将单元测试的速度提高一倍。
至于测试 UserInfo ,将其移动到自己的模块中并为其编写单元测试。
我刚刚带领一个团队完成上述变更。努力不到一天,我们的单元测试现在更快,更强大。
有关模块化和单元测试的讨论,请参阅Angular Developer Guide。
答案 1 :(得分:0)
很多时候,应用程序需要运行才能运行。像用户身份验证,翻译等。
对于这些情况,我通常会在每个规范中的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
})
})
我个人认为这种方法没有任何问题。测试会有一些重复,但认为没问题。
在某一点上您需要确认,当测试开始并且您加载模块时,应用程序在技术上正在被引导。这将启动您的一些应用程序代码。因此要么在测试中处理这些期望,要么更改您的应用程序。会说在测试中处理这个问题在大多数时候都会更好。
答案 2 :(得分:0)
在这种情况下,您的选择是:
$httpBackend.expectGET(...).respond(...)
UserInfo
的查找时使用$provide
to pass a different implementation 现在您最终可能会在多个位置重复此操作(具体取决于您的项目结构)。如果是这种情况,只需提取代码块,将其放在单独的JS文件中并将该文件包含在运行器中(如果使用grunt-contrib-jasmine之类的内容),将所述文件添加到vendor
列表中。)