模仿承诺在angularjs

时间:2014-07-23 09:22:46

标签: javascript angularjs unit-testing promise angular-promise

我试图嘲笑有角度的承诺,但是我得到了一些错误,比如undefined不是一个评估&spudOn' fileUploadService

我的控制器代码是

$scope.getUserFiles = function() {
    fileUploadService.retrieveUserFileNames('')
    .then(function(data) {
        $scope.userFiles =  data;
    });
};

服务代码,我从我的控制器中调用此方法

this.retrieveUserFileNames= function(userId) {
    var deferred = $q.defer();
    $http({
        method : "GET",
        url : "/retrieveExcelSheetNames",
        params : {
            "userId" : userId
        }
    }).success(function(data) {
        deferred.resolve(data);
    }).error(function(data, status) {
        deferred.reject(data);
    });
    return deferred.promise;
};

测试控制器代码

beforeEach(function() {
    inject(function(_fileUploadService_ , _$q_) {
        spyOn(scope, '$on');
        var deferred = _$q_.defer();
        fileUploadService = _fileUploadService_;
        deferred.resolve('resolveData');
        spyOn(fileUploadService, 'retrieveUserFileNames').andReturn(deferred.promise);
    });
});

it('is now a lot easier', function() {
   scope.getUserFiles();
   rootScope.$apply();
   expect(scope.userFiles).toBe('resolveData');
});

由于

1 个答案:

答案 0 :(得分:2)

当您在控制器中异步设置$scope.userFiles时,您需要等待$apply $http解决您的承诺。

假设您正在使用Jasmine,您可以使用异步规范表示法并注册$watch函数来测试您的控制器(没有测试代码,因此您可能需要对其进行调整,这也取决于其他修改范围变量)。

it('is now a lot easier', function(done) {
  scope.$watch('userFiles', function() {
     expect(scope.userFiles).toBe('resolveData');
     // Call done function to tell Jasmine that the spec has completed
     done();
  });

  scope.getUserFiles();
});

但是,我认为您应该测试您的服务,并且可以使用覆盖$ http的$httpBackend from ngMock并使您能够进行更深入的测试。在那里,您可以测试/监视对模拟后端的调用,并使用Jasmines done()函数来等待承诺。

这是一个简单的例子:

angular.module('mainApp', [])
  // Simple factory that uses $http for later use of $httpBackend mocking structure
  .factory('userManager', function($http, $q) {
     return $http.get('/api/users');
  });

规格:

describe('Simple factory that uses $http for later use of $httpBackend mocking structure', function() {
  // We need to use the module mainApp for all our tests
  beforeEach(module('mainApp'));

  // We use $httpBackend from ngMock module to mock our webservice call ($http will be overriden)
  // Also we need to inject inside of spec function as we need to use the async spec form with
  // a done function and this is not available using the inject to proxy the spec function
  it('should return desired user list', function(done) {

    inject(function($httpBackend, userManager) {

      $httpBackend.when('GET', '/api/users').respond({
        userList: [
          {user: 'User A'},
          {user: 'User B'},
          {user: 'User C'},
          {user: 'User D'},
        ]
      });
      $httpBackend.expectGET('/api/users');

      // userManager is returning a promise so we need to check asynchronously
      userManager.getUserList().then(function(result) {
        expect(result.data.userList).toContain(
          {user: 'User A'}, 
          {user: 'User B'}, 
          {user: 'User C'}, 
          {user: 'User D'}
        );

        // This call to the Jasmine done function is very important for asynchronous
        // unit tests as Jasmine is determining if the test is done by this call
        done();
      });

      $httpBackend.flush();

      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });
  });
});