javascript – Angular服务的Jasmine测试不解析延迟调用

我是Angular的新手,我正在测试一个运行API级别服务的Angular服务,该服务将大量调用包装到REST服务中.因为它正在处理HTTP请求,所以服务的两个部分都在使用promises,它似乎工作正常,但我无法对承诺的行为进行任何测试.

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

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处理延迟结果的方式似乎导致它失败.当我逐步完成测试时,正确设置了模拟对象的延迟变量,但从不调用服务中的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服务.您也应该注入该服务,并监视其功能.例如,像这样:

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',$q) {
     return {
       infoLoaded: false,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,$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);
  });
});

相关文章

kindeditor4.x代码高亮功能默认使用的是prettify插件,prett...
这一篇我将介绍如何让kindeditor4.x整合SyntaxHighlighter代...
js如何实现弹出form提交表单?(图文+视频)
js怎么获取复选框选中的值
js如何实现倒计时跳转页面
如何用js控制图片放大缩小