题 如何在Angular中覆盖$ httpBackend的模拟响应?


是否可以在模拟的$ httpBackend中覆盖或重新定义模拟响应?

我有这样的测试:

beforeEach(inject(function ($rootScope, $controller, _$httpBackend_) {
  $httpBackend = _$httpBackend_;

  //Fake Backend
  $httpBackend.when('GET', '/myUrl').respond({}); //Empty data from server
  ...some more fake url responses...     
 }

这对大多数情况都很好,但是我很少有测试,我需要为同一个URL返回不同的东西。但似乎一旦定义了when()。respond(),我就不能在这样的代码中改变它:

单个特定测试中的不同响应:

it('should work', inject(function($controller){
  $httpBackend.when('GET', '/myUrl').respond({'some':'very different value with long text'})

  //Create controller

  //Call the url

  //expect that {'some':'very different value with long text'} is returned
  //but instead I get the response defined in beforeEach
}));

我怎么做? 我的代码现在是不可测试的:(


25
2017-07-25 13:08


起源


通过将单元测试分成2个文件,我有点“解决”了它。每个文件都有自己的$ httpBackend.when()。respond()定义。这是一个丑陋的解决方案,但到目前为止我没有找到更好的解决方案。 - David Votrubec
我在使用ng的ng-describe模拟时遇到了同样的问题 github.com/kensho/ng-describe#mock-httpget  - 它似乎在它使用的引擎盖下 $httpBackend.when(...) 当我以后想要 deps.$httpBackend.expect("POST", "foobar") 和断言 deps.$httpBackend.verifyNoOutstandingExpectation(); 我收到一个错误 Error: Unsatisfied requests: POST foobar。似乎使用ng-describe $http mock使它无法使用 httpBackend.expect() 后来。 - jakub.g


答案:


文档似乎暗示了这种风格:

var myGet;
beforeEach(inject(function ($rootScope, $controller, _$httpBackend_) {
    $httpBackend = $_httpBackend_;
    myGet = $httpBackend.whenGET('/myUrl');
    myGet.respond({});
});

...

it('should work', function() {
    myGet.respond({foo: 'bar'});
    $httpBackend.flush();
    //now your response to '/myUrl' is {foo: 'bar'}
});

26
2017-08-12 07:29



使用whenGET工作,但它不必要地冗长。 - Zain Rizvi
参考:“when()返回一个带有响应方法的对象,该方法控制匹配请求的处理方式。您可以保存此对象供以后使用,并再次调用响应以更改匹配请求的处理方式。” - fracz
如果我有参考(如果myGet)在一些共享的“基类”文件中 beforEach 对于所有测试,我需要在其他特定测试/文件中引用它? - Felix


在响应中使用函数,例如:

var myUrlResult = {};

beforeEach(function() {
  $httpBackend.when('GET', '/myUrl').respond(function() {
    return [200, myUrlResult, {}];
  });
});

// Your test code here.

describe('another test', function() {
  beforeEach(function() {
    myUrlResult = {'some':'very different value'};
  });

  // A different test here.

});

12
2018-05-14 16:08





另一种选择是:

您可以使用 $httpBackend.expect().respond() 代替 $httpBackend.when().respond()

运用 expect(),您可以按两次相同的网址,并按照推送它们的顺序获得不同的响应。


9
2018-02-06 10:25



其实, when() 应该用于E2E测试而不是单元测试。 - germanio


在您的测试中使用.expect()而不是.when()

var myUrlResult = {};

beforeEach(function() {
  $httpBackend.when('GET', '/myUrl')
      .respond([200, myUrlResult, {}]);
});


it("overrides the GET" function() {
  $httpBackend.expect("GET", '/myUrl')
      .respond([something else]);
  // Your test code here.
});

6
2018-04-20 21:05



什么进来 [something else]?是个 .respond 在测试中需要吗? - Luke


您可以在执行测试的同一函数中重置“何时”响应。

it('should work', inject(function($controller){
    $httpBackend.when('GET', '/myUrl').respond({'some':'value'})
    // create the controller
    // call the url
    $httpBackend.flush();
    expect(data.some).toEqual('value');
});
it('should also work', inject(function($controller){
    $httpBackend.when('GET', '/myUrl').respond({'some':'very different value'})
    // create the controller
    // call the url
    $httpBackend.flush();
    expect(data.some).toEqual('very different value');
});

查看此plunker中的示例: http://plnkr.co/edit/7oFQvQLIQFGAG1AEU1MU?p=preview


0
2017-08-28 19:36



你的plunkr没有在beforeEach方法中设置响应,所以你实际上并没有覆盖任何值。你添加一个beforeEach,一切都开始失败: plnkr.co/edit/A44vSX?p=preview - Zain Rizvi