javascript - Angular 服务的 Jasmine 测试无法解决延迟调用

标签 javascript angularjs unit-testing jasmine angular-promise

我是 Angular 的新手,我正在努力测试一个 Angular 服务,该服务运行 API 级服务,该服务包装了对 REST 服务的一堆调用。因为它正在处理 HTTP 请求,所以服务的两个部分都在处理 promise ,而且似乎工作正常,但我无法对 promise 的行为进行任何测试。

我的服务代码的相关部分(粗略简化)如下所示:

angular.module('my.info')
 .service('myInfoService', function (infoApi, $q) {
    infoLoaded: false,
    allInfo: [],
    getInfo: function () {
        var defer = $q.defer();
        if (infoLoaded) {
            defer.resolve(allInfo);
        } else {
            infoApi.getAllInfo().then(function (newInfo) {
                allInfo = newInfo;
                infoLoaded = true;
                defer.resolve(allInfo);
            });
        }
        return defer.promise;
    }
});

当我设置我的模拟时,我有这样的东西:

   describe("Info Service ",
     function() {
        var infoService, infoRequestApi, $q;
        beforeEach(module("my.info"));
        beforeEach(function() {
            module(function($provide) {
                infoRequestApi = {
                   requestCount: 0,
                   getAllInfo: function() {
                       var defer = $q.defer(); 
                       this.requestCount++;
                       defer.resolve( [ "info 1", "info 2" ] );
                       return defer.promise;
                   }
                };
                $provide.value("infoApi", infoRequestApi);
            });
            inject(function( _myInfoService_, _$q_ ) {
                 infoService = _myInfoService_,
                 $q = _$q_;
            });
        });

        it("should not fail in the middle of a test", function(done) {
            infoService.getInfo().then( function(infoResult) {
                  // expectation checks.
                  expect( true ).toBeTrue();
              }).finally(done);
        });
   });

任何同步测试都可以顺利通过,但是当我尝试运行任何这样的测试时,我会收到一条消息:错误:超时 - 异步回调未在 jasmine.DEFAULT_TIMEOUT_INTERVAL 指定的超时内调用。

似乎 Angular.Mocks 处理延迟结果的方式导致它失败。当我逐步完成测试时,模拟对象的 defer 变量设置正确,但服务中的 then 语句从未被调用。我哪里错了?

最佳答案

简答题

您必须启动摘要循环。您可以使用 $rootScope.$apply() 来做到这一点。

it("should not fail in the middle of a test", inject(function($rootScope) {
  var actualResult = null;
  infoService.getInfo()
    .then(function(infoResult) { actualResult = infoResult; });
  $rootScope.$apply();
  expect(actualResult).toEqual(["info 1", "info 2"]);
}));

注意

我不会按照您的方式尝试创建伪造的 infoRequestApi 服务。您也应该注入(inject)该服务,并监视其功能。例如,像这样:

it("should not fail in the middle of a test", inject(function($rootScope, infoApi, $q) {
  var deferred = $q.defer();
  spyOn(infoApi, 'getAllInfo').and.returnValue(deferred.promise);
  var actualResult = null;
  var expectedResult = ["info 1", "info 2"];

  infoService.getInfo()
    .then(function(infoResult) { actualResult = infoResult; });

  deferred.resolve(expectedResult);
  $rootScope.$apply();
  expect(actualResult).toBe(expectedResult);
}));

重构

此外,您的代码可以稍微简化(未经测试,但接近我期望看到的)。

angular.module('my.info')
  .service('myInfoService', function (infoApi, $q) {
     return {
       infoLoaded: false,
       allInfo: [],
       getInfo: function () {
         var self = this;
         return this.infoLoaded ? $q.resolve(this.allInfo) :
           infoApi.getAllInfo().then(function (newInfo) {
             self.allInfo = newInfo;
             self.infoLoaded = true;
           });
     }
  };
});

describe("Info Service ", function() {
  var infoService, infoApi, $q, $rootScope;

  beforeEach(module("my.info"));
  beforeEach(inject(function( _myInfoService_, _$q_ _$rootScope_, _infoApi_) {
    infoService = _myInfoService_,
    $q = _$q_;
    $rootScope = _$rootScope_;
    infoApi = _infoApi_;
  });

  it("should do something", function() { // update message
    var deferred = $q.defer();
    spyOn(infoApi, 'getAllInfo').and.returnValue(deferred.promise);
    var actualResult = null;
    var expectedResult = ["info 1", "info 2"];

    infoService.getInfo()
      .then(function(infoResult) { actualResult = infoResult; });

    deferred.resolve(expectedResult);
    $rootScope.$apply();
    expect(actualResult).toBe(expectedResult);
  });
});

关于javascript - Angular 服务的 Jasmine 测试无法解决延迟调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39255323/

相关文章:

javascript - 如何创建一个框根据 2 个用户输入(方向和要移动的像素数)移动的功能

javascript - 根据窗口大小增加属性的负值

javascript - jQuery 从带有附加 HTML 的元素中获取值

javascript - Angular 中奇怪的路由行为

javascript - 使用 ng-repeat 有条件地应用过滤器

unit-testing - 如何针对参数序列测试谓词?

c++ - 使用 Google Mock 作为指向调用中分配的数组的指针?

javascript - 单元测试 React 点击外部组件

javascript - 如何将背景图像从本地文件夹添加到容器

javascript - 操作引发事件的 DOM 元素