【问题标题】:AngularJS Unit tests broken after $httpBackend decoratorAngularJS 单元测试在 $httpBackend 装饰器之后被破坏
【发布时间】:2016-10-07 07:55:30
【问题描述】:

所以我遇到了这样一种情况,我们不得不访问一个不受我们控制的“restful”服务,为了在 GET 调用中从服务中获取 json,我们必须传递 Content-Type="application/json “在标题中。唯一的问题是 Angular 从 GET 的请求标头中剥离了 Content-Type。我发现一篇博客文章建议在 $httpBackend 上使用装饰器,它允许我们在调用发送之前拦截调用并添加回内容类型:

angular
.module('MyApp')
.decorator('$httpBackend', [
  '$delegate', function($delegate) {
      return function() {
          var contentType, headers;
          headers = arguments[4];
          contentType = headers != null ? headers['X-Force-Content-Type'] : null;
          if (contentType != null && headers['Content-Type'] == null)
            headers['Content-Type'] = contentType;

          return $delegate.apply(null, arguments);
      };
  }]);

所以,效果很好!现在我们的问题是它破坏了我们使用模拟 $httpBackend 服务的所有单元测试。我们得到的唯一错误是“未定义”。

例如。单元测试方法:

it('should return service.model.error if service returns an exception code from EndProject', 
 inject(function($httpBackend) {
      var mockResponse = sinon.stub({ 'exception': 'Unable to retrieve service data' });
      $httpBackend.whenPUT(this.endProjectUrl).respond(mockResponse);
      var data;
      this.service.EndProject().then(function(fetchedData) {
           data = fetchedData;
      });

      $httpBackend.flush();
      expect(data.error.state).toBe(true);
      expect(data.error.message).toEqual('Unable to retrieve service data');
 }));

PhantomJS 2.1.1 (Mac OS X 0.0.0) 如果服务从 EndProject FAILED 返回异常代码,则 projectService EndProject 应返回 service.model.error 不明确的 /Users/mlm1205/Documents/THDSource/bolt-projects/html_app/src/app/components/services/project/projectService.spec.js:213:41 调用@/Users/mlm1205/Documents/THDSource/bolt-projects/html_app/bower_components/angular/angular.js:4560:22 workFn@/Users/mlm1205/Documents/THDSource/bolt-projects/html_app/bower_components/angular-mocks/angular-mocks.js:2518:26

【问题讨论】:

    标签: angularjs unit-testing jasmine httpbackend


    【解决方案1】:

    虽然我将 estus 的答案标记为解决方案,但纯粹基于我的问题......最终,最终这不是我们采用的最终结果。在看不到森林的情况下,最简单的解决方案是在 $http 调用的配置中添加一个空数据元素。我之前尝试过,但它不起作用(或者看起来如此),但在再次使用它之后,它确实起作用了,我们能够从应用程序中删除装饰器。

    return $http.get(getItemInformationUrl + params, { dataType: 'json', data: '', headers: {'Content-Type': 'application/json'} }).then(getItemInformationCompleted).catch(getItemInformationFailed);
    

    【讨论】:

      【解决方案2】:

      列出的装饰器涵盖了简单的猴子修补场景,其中修补的函数不是构造函数,也没有静态属性和方法。

      $httpBackend in ng module 也是如此,它只是一个没有额外属性的工厂函数。

      这不适用于覆盖$httpBackend 并具有静态方法at least some of them are documentedngMockngMockE2E 模块。

      这意味着猴子修补工厂函数的一般安全配方(它不包括不可枚举和继承的属性)是

      app.decorator('$httpBackend', ['$delegate', function ($delegate) {
          var $httpBackend = function () {
              ...
              return $delegate.apply(null, arguments);
          };
          angular.extend($httpBackend, $delegate);
          return $httpBackend;
      }]);
      

      不管怎样,将应用程序模块化到可以在没有过多移动部件的情况下单独测试单元的水平是一个好习惯(这个问题是一个表达性的例子,为什么这很重要)。方便有app(在生产环境中引导)、app.e2e(在e2e测试中引导)、app.common(共同点)、app.unitA(在app.common中加载,可以在单元测试中单独加载)等。

      大多数应用程序范围的代码(configrun 块,路由)可以移动到单独的模块中,并仅加载到直接依赖它们的模块中。除非这是测试 decorator 单元本身的规范,否则不应加载装饰器模块。

      在调试规范错误时还要注意Chrome may offer superior experience 比 PhantomJS。

      【讨论】:

      • 我试过了,但呼叫仍在进行,内容类型被剥离。我有一个拦截器,它注入了我们的负载均衡器所需的另一个标头,并且已经为 Content-Type 尝试了这个,但它没有用。我通过拦截器代码进行调试并验证它确实添加了标头,但是当您在“网络”选项卡(Chrome 开发工具)中查看实际调用时,Content-Type 标头消失了。
      • 我想我以前误解了你的情况。你有一个现有的生产装饰器,它可以为你工作,但在单元测试中失败,尽管单元测试没有提出真正的请求。我已经更新了答案。请参阅 PhantomJS 上的评论,我很确定错误消息因此而残缺,并且由于$httpBackend.whenPUT(...) 而应该说类似undefined is not a function
      • 是的,对不起,如果我没有说得更清楚。我将尝试根据您的建议进行一些尝试。我真的想反击创建服务的团队来更改它,因为在 GET 请求中要求该标头是荒谬的。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-12-14
      • 2016-12-30
      • 2013-05-01
      • 2011-03-02
      • 1970-01-01
      • 2012-04-26
      • 1970-01-01
      相关资源
      最近更新 更多