【问题标题】:AngularJS, spies, and resolving promisesAngularJS、间谍和解决承诺
【发布时间】:2014-07-30 23:30:54
【问题描述】:

我正在为控制器编写测试。一种方法调用服务中的方法,该方法利用了承诺。在我的测试中,我嘲笑了服务,并且(我认为)正确地嘲笑了承诺。我一直在关注这个博客条目:http://codingsmackdown.tv/blog/2012/12/28/mocking-promises-in-unit-tests/

这里是测试代码:

describe('Controller: ResultsController', function () {
    'use strict';
    var ctrl;
    var ResultsServiceMock;
    var RouteServiceMock;
    var $scope;
    var mockResults;
    var mockPromise;
    var q;
    var deferred;

    beforeEach(module('waApp'));

    beforeEach(function() {
        ResultsServiceMock = {
            get: function(query) {
                deferred = q.defer();
                return deferred.promise;
            }
        };
        RouteServiceMock = {
            getParams: function() {
            }
        };
    });

    beforeEach(inject(function(
        $rootScope,
        $controller,
        $q
    ) {
        $scope =  $rootScope.$new();
        q = $q;
        ctrl = $controller('ResultsController', {
            $scope: $scope,
            results: ResultsServiceMock,
            route: RouteServiceMock
        });
    }));

    it('Should simulate requesting results from the api', function() {
        spyOn(ResultsServiceMock, 'get').andCallThrough();
        spyOn(RouteServiceMock, 'getParams').andReturn({input:'hamburger'});
        $scope.getResults({input:'hamburger'});  // TODO give params.  try query()
        deferred.resolve();
        $scope.$root.$digest();
        expect($scope.getResults).toHaveBeenCalled();
    });
});

但是,当我运行测试时,我收到以下错误:

Chrome 35.0 (Mac) Controller: ResultsController Should simulate requesting results from the api FAILED

TypeError: Cannot read property 'resolve' of undefined
    at null.<anonymous> (/Users/maryc/wa/app/results/results-controller_test.js:70:17)

Chrome 35.0 (Mac): Executed 14 of 14 (1 FAILED) (0.313 secs / 0.093 secs)

我不明白这个错误是从哪里来的;是因为对 ResultsServiceMock 的间谍调用不起作用吗?任何帮助将不胜感激。

getResults函数如下:

 $scope.getResults = function(params) {¬
     $scope.$emit('startGetResults');¬
     $scope.loading = true;¬
     $scope.podRequestStatus.init = false;¬
     $scope.podRequestStatus = {¬
         async: {}¬
     };¬

     var didyoumeans;¬
     if(params.didyoumeans) {¬
         didyoumeans = params.didyoumeans;¬
         delete params.didyoumeans;¬
     }¬

     ResultsService.get(params).success(function(result) {¬
         $scope.$emit('getResultsSuccess');¬
         if(!_.isUndefined(didyoumeans)) {¬
             $scope.results.queryresult.didyoumeans = didyoumeans;¬
         } else if(!_.isUndefined(result.queryresult.didyoumeans)) {¬
             if (!_.isArray(result.queryresult.didyoumeans)){¬
                 result.queryresult.didyoumeans = [result.queryresult.didyoumeans];¬
             }¬
             $scope.getResults({input: result.queryresult.didyoumeans[0].val, didyoumeans:    result.queryresul    t.didyoumeans});¬
             return;¬
         }¬

         $scope.loading = false;¬

         $scope.podRequestStatus.init = true;¬

         if(result.queryresult.success === false) {  //TODO is this result.results.queryresult.success??¬
             if(result.queryresult.error !== false) {¬
                 $log.error('Results error', 'code: ' + result.queryresult.error.code, 'msg: ' + result.quer    yresult.error.msg);¬
                 switch (result.queryresult.error.code){¬
                     case '3000':¬
                         $location.url('/blockedip');¬
                         break;¬
                 }¬

                 return;¬
             }¬ 

             if($scope.results.queryresult.examplepage && $scope.results.queryresult.examplepage.category) {    ¬
                 $scope.examples();¬
             }¬
             // convert tips to an array if we have a single item¬
             if($scope.results.queryresult.tips && !_.isArray($scope.results.queryresult.tips)){¬
                 $scope.results.queryresult.tips = [$scope.results.queryresult.tips];¬
             }¬
             $log.error('Results error');¬
             return;¬
         }¬

         $scope.results.queryresult.pods = _.map($scope.results.queryresult.pods, function(pod) {¬
             pod.priority = PodService.priority.initial;¬
             return pod;¬
         });¬

         if ($scope.results.queryresult.sources && _.where($scope.results.queryresult.sources, {'text':'Fina    ncial data'})) {¬
             $scope.$emit('financialData', true);¬
         } else {¬
             $scope.$emit('financialData', false);¬
         }¬ ¬

         $scope.asyncPods(PodService.priority.async, 'async');¬
         $scope.recalculate();¬
         $scope.related();¬

     }).error(function() {¬
         $log.error('error occurred during ResultsService.get call in ResultsController');¬                 
     });¬
 };¬

函数 asyncPods、recalculate 和相关是 ResultsController 中的其他三个方法。

已编辑:修复了第一个错误后,我现在在运行测试时收到以下错误:

Chrome 35.0 (Mac) Controller: ResultsController Should simulate requesting results from the api FAILED
TypeError: undefined is not a function
    at Scope.$scope.getResults (/Users/maryc/wa/.tmp_test/results-controller.js:222:36)
    at null.<anonymous> (/Users/maryc/wa/app/results/results-controller_test.js:67:16)

此错误来自调用 ResultsService.get() 的 getResults() 开头的行。这似乎暗示我的承诺要么没有得到解决,要么调用 $scope.getResults() 以某种方式失败?

ResultsService的.get()函数代码为:

      get: function(query) {¬
          this.reset();¬
          return ApiService.get({¬
              params: UtilService.merge(query, {¬
                  async: true,¬
                  scantimeout: 1,¬
                  formattimeout: 8,¬
                  parsetimeout: 5,¬
                  format: 'image,plaintext,imagemap',¬
                  banners: 'true'¬
              }),¬
              timeout: abort.promise,¬
              type: 'init',¬
              cache: UserService.user.cacheResults¬
          }).success(function(data){results.queryresult = data.queryresult;});¬                              
      },¬

我现在想知道问题是否在于 .get 本身包含一个承诺?

【问题讨论】:

  • 请检查ResultsServiceMock.get()是否真的被调用了。
  • 所以看起来 ResultsServiceMock.get() 实际上并没有被调用,但我不知道为什么会这样。
  • 你能展示一下$scope.getResults()函数的实现吗?我们可以从那里开始找出问题所在。
  • 当然,我将编辑问题以包含 getResults() 代码。

标签: angularjs jasmine


【解决方案1】:

根据您目前提供的信息,这是我对可能导致问题的猜测。

在您的$scope.getResults() 方法中,您调用了ResultsServiceget() 方法,所以看起来服务的名称是ResultsService

但在单元测试中,您将ResultsServiceMock 传递为results

ctrl = $controller('ResultsController', {
  $scope: $scope,
  results: ResultsServiceMock,
  route: RouteServiceMock
});

应该是ResultsService,而不是像这样:

ctrl = $controller('ResultsController', {
  $scope: $scope,
  ResultsService: ResultsServiceMock,
  RouteService: RouteServiceMock
});

这个改变之后,你可能会遇到另一个问题,但它应该会带你通过错误:

TypeError: Cannot read property 'resolve' of undefined

希望这会有所帮助。

【讨论】:

  • 你是对的,这确实解决了我最初的问题!但是,它确实导致了另一个我不明白的错误;我已编辑问题以反映新错误。如果您也能以任何方式查看那个,我将不胜感激。
  • 我认为你应该开始在 DevTools 中调试单元测试。您可能会看到 this post 以开始了解如何设置断点。
  • 对于第二个错误,我没有看到RouteService.getParams() 在任何地方被调用,请包括拨打电话的代码和RouteService.getParams() 本身。
  • 所以我已经解决了大部分出现的错误,最终变成了愚蠢的错误。我留下了以下错误:Chrome 35.0 (Mac) Controller: ResultsController 应该模拟从 api 请求结果 FAILED TypeError: undefined is not a function at Scope.$scope.getResults(/Users/maryc/wa/.tmp_test/ results-controller.js:222:36) 在 null. (/Users/maryc/wa/app/results/results-controller_test.js:67:16) 这是 getResults 中调用 ResultsService.get 的行暗示我的承诺没有得到解决或 $scope.getResults 失败。
  • 第 222 行的 results-controller.js 中有什么内容?错误表明它来自那里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多