2016-06-07 34 views
2

所以我遇到了一个情况,就是我们不得不击中不在我们控制下的“宁静”服务,为了从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服务的所有单元测试。我们得到的唯一错误是“未定义”。

Ex。单元测试方法:

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)projectService EndProject应该返回service.model.error如果服务返回从EndProject异常代码FAILED 未定义 /用户/ mlm1205 /Documents/THDSource/bolt-projects/html_app/src/app/components/services/project/projectService.spec.js:213:41 invoke @/Users/mlm1205/Documents/THDSource/bolt-projects/html_app/bower_components/angular/angular.js:4560:22 [email protected]/Users/mlm1205/Documents/THDSource/bolt-projects/html_app/bower_components/angular-mocks/angular-mocks.js:2518:26

回答

0

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

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

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

这对于$httpBackend in ng module是正确的,它只是一个没有额外特性的工厂函数。

这不适用于ngMockngMockE2E模块,它们会覆盖$httpBackend并且具有静态方法at least some of them are documented

这意味着,总体上是安全的配方(不包括非枚举和继承的属性)的猴子修补工厂函数无论是

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。

+0

我试过了,但是这个调用仍然在剥离Content-Type。我有一个拦截器,它注入了我们的负载均衡器所需的另一个头,并且已经尝试过这种Content-Type,但它不起作用。我通过拦截器代码进行调试,并确认它确实添加了标题,但是当您查看“网络”选项卡(Chrome开发工具)中的实际调用时,Content-Type标头消失了。 – Shaggy13spe

+0

我想我以前曾经误解过你的情况。尽管事实上单元测试没有提出真正的请求,但您已经有了适用于您的现有生产装饰器,但在单元测试中失败。我已经更新了答案。看到关于PhantomJS的评论,我很确定错误信息因此被破坏,并且应该说'undefined不是一个函数',因为'$ httpBackend.whenPUT(...)'。 – estus

+0

是的,抱歉,如果我没有更清楚。我会尽量按照你的建议玩一下。我真的想要推回创建该服务的团队来改变它,因为在GET请求上需要该头部是很荒谬的。 – Shaggy13spe