如何避免"意外请求:GET"在app.run循环期间进行的调用,而不更改我的所有单元测试

时间:2016-01-13 20:55:01

标签: angularjs unit-testing jasmine karma-runner karma-jasmine

我的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块上写下这两行?

3 个答案:

答案 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)

在这种情况下,您的选择是:

  1. 指出
  2. 时使用$httpBackend.expectGET(...).respond(...)
  3. 执行UserInfo的查找时使用$provide to pass a different implementation
  4. 现在您最终可能会在多个位置重复此操作(具体取决于您的项目结构)。如果是这种情况,只需提取代码块,将其放在单独的JS文件中并将该文件包含在运行器中(如果使用grunt-contrib-jasmine之类的内容),将所述文件添加到vendor列表中。)