简体   繁体   English

如何测试一个返回$ timeout promise并带有$ http.get的AngularJS工厂方法?

[英]How to test an AngularJS factory method that returns a $timeout promise with a $http.get inside?

For specific purposes, I had to write a factory method that returns a $timeout promise that, inside of it, returns a $http.get promise. 为了特定的目的,我必须编写一个工厂方法,该方法返回一个$timeout承诺,在其中,它返回一个$http.get承诺。

I want to test if a call to my factory method will call the $http.get with the correct mocked URL ( mocked-get-path ). 我想测试对我的工厂方法的调用是否将使用正确的$http.get URL( mocked-get-path )调用$http.get

Here is my factory code: 这是我的工厂代码:

(function() {
  'use strict';

  angular.module('MyApp', []);

  angular.module('MyApp')
    .constant("ROUTES", {
      get: "real-get-path"
    });

  angular.module('MyApp')
    .factory('MyFactory', ['$http', '$timeout', 'ROUTES', MyFactory]);

  function MyFactory($http, $timeout, ROUTES) {
    return {
      myGet: function(id) {
        var random = Math.random() * 1000;
        return $timeout(function () {
            return $http.get(ROUTES.get, {
              params: { id: id }
            })
              .then(function() {
                return response.data;
              });
        }, random);
      }
    };
  }
})();

And my test specification: 和我的测试规范:

describe('simple factory test', function() {
  var $http, $timeout, scope, MyFactory;

  var ROUTES = {
      get: 'mocked-get-path'
  };

  beforeEach(module('MyApp', function ($provide) {
      $provide.constant('ROUTES', ROUTES);
  }));

  beforeEach(module('MyApp'));

  beforeEach(inject(function(_$rootScope_, _$http_, _$timeout_, _MyFactory_) {
    scope = _$rootScope_.$new();
    $http = _$http_;
    $timeout = _$timeout_;
    MyFactory = _MyFactory_;
  }));

  it('should use ROUTES.get on method myGet', function(done) {
    spyOn(Math, "random").and.returnValue(0.01);

    MyFactory.myGet('elem1')
      .then(function(res) {
        expect($http.get).toHaveBeenCalledWith(ROUTES.get);
        done();
      });
    $timeout.flush();
  });
});

You can see that I tried to write a expect for the $http.get inside the then , but it didn't work. 你可以看到,我试着写一个expect$http.getthen ,但没有奏效。

I receive this error from Jasmine: 我从茉莉花收到此错误:

Error: Unexpected request: GET mocked-get-path?id=elem1 错误:意外请求:GET嘲笑的获取路径?id = elem1

I made a Plunker: https://plnkr.co/edit/Ia6Q6GvKZOkNU2B8GrO1 我做了一个傻瓜: https ://plnkr.co/edit/Ia6Q6GvKZOkNU2B8GrO1

What am I doing wrong? 我究竟做错了什么?

When testing $http you are going to want to use $httpBackend.when 测试$http您将要使用$httpBackend.when

In your case: 在您的情况下:

it('should use ROUTES.get on method myGet', function(done) {
  spyOn(Math, "random").and.returnValue(0.01);
  spyOn($http, "get").and.callThrough();

  MyFactory.myGet('elem1')
    .then(function(res) {
      expect($http.get).toHaveBeenCalledWith(ROUTES.get, {params: {id: 'elem1'}});
      done();
    });

  $httpBackend
    .when('GET', "mocked-get-path?id=elem1")
    .respond(200, { foo: 'bar' });

  $timeout.flush(100);

  $timeout.verifyNoPendingTasks();
  $httpBackend.flush();

});

This will cause $httpBackend to complete your request successfully allowing your .then with the expect to execute. 这将导致$httpBackend完成您的请求成功地让你的.then与期望执行。

Plunkr showing solution Plunkr显示解决方案

ngmock fundamentals ngmock基础知识

I hope this helps! 我希望这有帮助! Have a nice day! 祝你今天愉快!

One approach is to add expected calls to the httpBackend like this: 一种方法是将预期的调用添加到httpBackend中,如下所示:

var  $httpBackend;
beforeEach(function () {
        inject(function ( _$httpBackend_ ) {
            $httpBackend = _$httpBackend_;
        }

.... ....

it('....', function(){
    //before the rest of the test
    $httpBackend.expectGET('url').respond("OK");

});

https://docs.angularjs.org/api/ngMock/service/$httpBackend https://docs.angularjs.org/api/ngMock/service/$httpBackend

So, your code will look like this: 因此,您的代码将如下所示:

describe('simple directive', function() {
  var $http, $timeout, scope, MyFactory, $httpBackend;

  var ROUTES = {
      get: 'mocked-get-path'
  };

  beforeEach(module('MyApp', function ($provide) {
      $provide.constant('ROUTES', ROUTES);
  }));

  beforeEach(module('MyApp'));

  beforeEach(inject(function(_$rootScope_, _$http_, _$timeout_, _MyFactory_, _$httpBackend_) {
    $httpBackend = _$httpBackend_;
    scope = _$rootScope_.$new();
    $http = _$http_;
    $timeout = _$timeout_;
    MyFactory = _MyFactory_;
  }));

  it('should use ROUTES.get on method myGet', function(done) {
    spyOn(Math, "random").and.returnValue(0.01);
    $httpBackend.expectGET("mocked-get-path?id=elem1").respond("OK");
    MyFactory.myGet('elem1')
      .then(function(res) {
        expect($http.get).toHaveBeenCalledWith(ROUTES.get);
        done();
      });
    $timeout.flush();
  });
});

It will get you passed the unexpected GET error. 它会让您通过意外的GET错误。 But then there is another issue about not getting the call within the timeout period. 但是还有另一个问题,就是在超时时间内无法接听电话。

And, providing that now you are expecting a correct call at the $httpBackend, you can simplify your test case by removing the async nature of it like this: 并且,假设现在您期望在$ httpBackend处进行正确的调用,则可以通过删除它的异步特性来简化测试用例,如下所示:

(remove the paremeter done, and simplify the test code) (删除参数表,并简化测试代码)

it('should use ROUTES.get on method myGet', function() {
    spyOn(Math, "random").and.returnValue(0.01);
    $httpBackend.expectGET("mocked-get-path?id=elem1").respond("OK");
    MyFactory.myGet('elem1');
    $timeout.flush();
  });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM