使用Jasmine测试Angular异步服务

时间:2014-09-13 18:50:12

标签: angularjs jasmine integration-testing

我正在尝试使用Jasmine(集成测试)测试一个真正的http调用,但是当我调用一个使用$ http.get的方法时,它会超时并且服务器永远不会被调用。

我知道我应该注入$ http的实现,但不确定应该在哪里发生。

searchSvc

app.service('searchSvc', ['$http', '$q', searchSvc]);
function searchSvc($http, $q) {
  return {
    search: function(text) {
      console.log('svc.search called with ', text); // this does get called 
      return $q.when($http.get('/search/' + text));
    }
  };
}

searchSpec

describe("searchTest", function() {
  var ctrl, svc, $http;

  beforeEach(function () {
      module('testApp');
    inject(function(_$controller_, searchSvc, _$http_){
      ctrl = _$controller_('searchCtrl');
      svc = searchSvc;
      $http = _$http_;
    })
  });

  it('test server search', function(done) {
      svc.search('re').then(function(result) {
        console.log('promise then'); // this never gets called, because server never gets called
        expect(result).not.toBeNull();
        expect(result.data).not.toBeNull();
        expect(result.data.length).toBeGreaterThan(0);

        done();
      });
  });

3 个答案:

答案 0 :(得分:0)

如果你使用promises,你可以在http://entwicklertagebuch.com/blog/2013/10/how-to-handle-angularjs-promises-in-jasmine-unit-tests/

找到如何处理它们的方法

答案 1 :(得分:0)

这是一种假设,但如果你同时包含ngMock和& ngMockE2E模块作为您的应用模块的依赖项(ngMock需要在依赖项列表中ngMockE2E之前),您应该能够使用$httpBackend提供的ngMockE2E服务{1}}模块passThrough在您的测试规范中对搜索api调用实际后端。

尝试这样的事情,看看它是否有效:

describe("searchTest", function() {
  var ctrl, svc, $httpBackend;

  beforeEach(function () {
    module('testApp');
    inject(function(_$controller_, searchSvc, _$httpBackend_){
      $httpBackend = _$httpBackend_;
      ctrl = _$controller_('searchCtrl');
      svc = searchSvc;
    });
  });

  it('test server search', function(done) {
      $httpBackend.whenGET(/^\/search\//).passThrough();
      svc.search('re').then(function(result) {
        console.log('promise then'); // this never gets called, because server never gets called
        expect(result).not.toBeNull();
        expect(result.data).not.toBeNull();
        expect(result.data.length).toBeGreaterThan(0);

        done();
      });
  });
});

答案 2 :(得分:0)

这是我在使用ngMock进行单元测试时用来进行真正的HTTP调用的解决方案。我主要用它来调试,完成测试,获取JSON示例等。

我在博客上写了一篇关于解决方案的更详细的帖子:How to Unit Test with real HTTP calls using ngMockE2E & passThrough

解决方案如下:

angular.mock.http = {};

angular.mock.http.init = function() {

  angular.module('ngMock', ['ng', 'ngMockE2E']).provider({
    $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
    $log: angular.mock.$LogProvider,
    $interval: angular.mock.$IntervalProvider,
    $rootElement: angular.mock.$RootElementProvider
  }).config(['$provide', function($provide) {
    $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
    $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
    $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
    $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
    $provide.decorator('$controller', angular.mock.$ControllerDecorator);
  }]);

};

angular.mock.http.reset = function() {

  angular.module('ngMock', ['ng']).provider({
    $browser: angular.mock.$BrowserProvider,
    $exceptionHandler: angular.mock.$ExceptionHandlerProvider,
    $log: angular.mock.$LogProvider,
    $interval: angular.mock.$IntervalProvider,
    $httpBackend: angular.mock.$HttpBackendProvider,
    $rootElement: angular.mock.$RootElementProvider
  }).config(['$provide', function($provide) {
    $provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
    $provide.decorator('$$rAF', angular.mock.$RAFDecorator);
    $provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
    $provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
    $provide.decorator('$controller', angular.mock.$ControllerDecorator);
  }]);

};

在ngMock之后包含此源文件,例如:

<script type="text/javascript" src="angular.js"></script>
<script type="text/javascript" src="angular-mocks.js"></script>
<!-- this would be the source code just provided -->
<script type="text/javascript" src="ngMockHttp.js"></script>

如何编写测试?

  describe('http tests', function () {

    beforeEach(module('moviesApp'));

    var $controller;
    var $httpBackend;
    var $scope;

    describe('real http tests', function() {

      beforeEach(angular.mock.http.init);
      afterEach(angular.mock.http.reset);

      beforeEach(inject(function(_$controller_, _$httpBackend_) {
        $controller = _$controller_;
        $scope = {};
        $httpBackend = _$httpBackend_;

        // Note that this HTTP backend is ngMockE2E's, and will make a real HTTP request
        $httpBackend.whenGET('http://www.omdbapi.com/?s=terminator').passThrough();
      }));

      it('should load default movies (with real http request)', function (done) {
        var moviesController = $controller('MovieController', { $scope: $scope });

        setTimeout(function() {
          expect($scope.movies).not.toEqual([]);
          done();
        }, 1000);

      });

    });

  });

它是如何工作的?

它使用ngMockE2E的$ httpBackEndProvider版本,它为我们提供了我们在测试中使用的 passThrough 函数。这就像名称所示,并允许本机HTTP调用通过。

我们需要在没有虚假版本的$ BrowserProvider的情况下重新定义ngMock模块,因为这会阻止使用ngMock的单元测试中的真实HTTP调用。