【问题标题】:How do I unit test an AngularJS controller that relies on a promise?如何对依赖于 Promise 的 AngularJS 控制器进行单元测试?
【发布时间】:2014-05-15 20:46:25
【问题描述】:

在我的控制器中,我有:

  $scope.index = function() {
    CompanyService.initialized.then(function() {
      var company_id = CompanyService.getCompany()._id;
      LocationService.list(company_id, $routeParams.location_parent_id).then(function(response) {
        if(response.data.status === 'ok') {
          $scope.locations = response.data.locations;
          $scope.current_location = response.data.location || null;
        }
      });
    });
  }

所以应该得到LocationService列表,测试如下:

it('should get locations', function() {
  $httpBackend.when('GET', '/api/v1/location/list').respond({status: 'ok', locations: ['loc1', 'loc2']})
  $scope.index();
  expect($scope.locations.length).toEqual(2);

然而,这永远不会发生,因为CompanyService 有一个在单元测试中永远不会得到解决的承诺。如何模拟 CompanyService 的返回承诺或绕过它?

【问题讨论】:

    标签: javascript angularjs unit-testing promise


    【解决方案1】:

    使用 Jasmine 中的 createSpyObj 方法简单地模拟调用:

    describe('one test', function(){
    
     var deferred, CompanyService, $scope;
    
     beforeEach(inject(function ($q, $rootScope, $controller) {
      params = {
            $scope: $rootScope.$new(),
            CompanyService = jasmine.createSpyObj('CompanyService', ['initialized'])
      }
      params.CompanyService.initialized.andCallFake(function () {
          deferred = $q.defer();
          return deferred.promise;  //will fake to return a promise, in order to reach it inside the test
        });
      $controller('YourController', params);
     });
    
     it('my test', function(){
         deferred.resolve({}); //empty object returned for the sample but you can set what you need to be returned
         $scope.$digest(); //important in order to resolve the promise
        //at this time, the promise is resolved! so your logic to test can be placed here
     });
    });
    

    【讨论】:

    • 我收到TypeError: 'undefined' is not an object (evaluating 'deferred.resolve')
    • @Shamoon 我更新了,你使用的是完全相同的代码吗?确保 deferred 变量在您的测试中是可访问的,如代码所示。
    • 如何使用$injector 设置来完成此操作?我使用nathanleclaire.com/blog/2013/12/13/… 作为我的测试基准
    • @Shamoon 无需显式使用$injector。但是如果你真的想要,你可以这样做:beforeEach(inject(function($injector) { $rootScope = $injector.get('$rootScope'); // .......
    • 对,我知道如何使用$injector 获取$rootScope 等,但是这个间谍业务呢?
    【解决方案2】:

    简答:在调用$scope.$index();后添加$scope.$apply();

    长答案:您的问题可能是由于 $q 传播承诺解决方案的方式。实际上,只有在执行 $digest 时才会发生解析。我不知道为什么在测试期间不执行 $digest 而在常规使用期间不执行此解决方案,但无论如何在此处的 Angular 文档中建议使用此解决方案:https://docs.angularjs.org/api/ng/service/$q。

    此外,您还可以直接使用 $scope.$digest(); 而不是 $scope.$apply();,因为 $apply 只是一个包装器,它运行在调用 $digest 之前传递给它的任何函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-04-29
      • 1970-01-01
      • 2019-04-26
      • 1970-01-01
      • 1970-01-01
      • 2014-05-29
      • 1970-01-01
      相关资源
      最近更新 更多