Angular和Karma测试使用$ resource并返回promise

时间:2015-07-16 20:44:08

标签: angularjs unit-testing karma-jasmine angular-resource

我正在尝试使用Jasmine为我的AngularJS用户服务开发单元测试,该服务依赖于$ resource。我的测试是:

'use strict';

describe("User Service Test", function() {

var service;
var mockLoginUser = { email: 'hidden', password: "hidden" };

  beforeEach(module('flightlottery.userApi'));
  beforeEach(inject(function(User) {
    service = User;
  //  $scope = _$scope_;
    //http = $httpBacked;
  }));

it('should fetch login a user', function(done) {
    var testUser = function(user) {
        console.log('callback called');
      //expect(user.email).toBe(mockLoginUser.email);
      //expect(user.password).toBe(mockUser.password);
    };

    var failTest = function(error) {
      expect(error).toBeUndefined();
    };

    //http.expectPost('/users/login', mockLoginUser).respond(200,'');

    //http.expectGET('/employees/1').respond(200,mockEmployee);

    service.login(mockLoginUser)
      .$promise.then(testUser)
      .catch(failTest)
      .finally(done);

//    $scope.$apply;
    //http.flush();
});
});

当我运行测试时,我收到以下错误。

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

任何人都可以告诉我如何做到这一点?我觉得它很混乱。

谢谢!

***编辑: 这是我的用户服务。我想测试登录方法。

angular.module('flightlottery.userApi', ['ngResource']).
factory('User', function($resource, $rootScope) {

  var current_user;

  var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
    query: {method:'GET', params: {method:'index'}, isArray:true },
    save: {method:'POST', params: {method:'save'} },
    get: {method:'GET', params: {method:'edit'} },
    phistory: {method:'GET', params: {method:'history'}, isArray:true },
    remove: {method:'DELETE', params: {method:'remove'} },
    login: {method:'POST', params: {method:'login'} },
    logout: {method:'POST', params: {method:'logout'} },
    register: {method:'POST', params: {method:'register'} }
  });


  User.setCurrentUser = function(user) {
    //var self = this;
    current_user = user;
    $rootScope.$broadcast('user:updated',user);
    //console.log(self.current_user_id);
  }

  User.getCurrentUser = function() {
    //var self = this;
    return current_user;
    //console.log(self.current_user_id);
  }

  User.registerUser = function(cb) {
    //console.log(cb);
    return User.register(cb);
  }

  User.play_history = function(cb) {
    //console.log(cb);
    return User.phistory(cb);
  }

  User.loginUser = function(cb) {
    return User.login(cb);
  }

  User.logoutUser = function(cb) {
    current_user = null;
    return User.logout(cb);
  //  return User.logout();
    ///return User.save({id: this.id},
        //angular.extend({}, this, {id:undefined}), cb);
  };

  User.prototype.update = function(cb) {
    return User.save({id: this.id},
        angular.extend({}, this, {id:undefined}), cb);
  };

  User.prototype.destroy = function(cb) {
    return User.remove({id: this.id}, cb);
  };

  return User;
});

1 个答案:

答案 0 :(得分:2)

在尝试猜测你想要完成什么之后,几乎没有重构 - 结果看起来像这样。

我总是想知道我们想要测试什么,以及我们想要证明测试通过或失败的原因。

带有依赖关系的版本

angular.module('flightlottery.userApi', ['ngResource'])
  .factory('User', function($resource) {
    var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
      login: {
        method: 'POST',
        params: {
          method: 'login'
        }
      }
    });

    User.loginUser = function(cb) {
      return User.login(cb);
    }

    return User;
  })

describe("User Service Test", function() {

  var service;
  var queryDeferred;
  var mockLoginUser = {
    email: 'hidden',
    password: "hidden"
  };

  var scenarios = {
    success: function(user) {
      expect(user.email).toBe(mockLoginUser.email);
      expect(user.password).toBe(mockLoginUser.password);
    },

    fail: function(error) {
      expect(error).toBeDefined();
    }
  }

  beforeEach(module('flightlottery.userApi'));

  beforeEach(inject(function(_$rootScope_) {
    $rootScope = _$rootScope_;
  }));

  beforeEach(inject(function($q) {
    queryDeferred = $q.defer();
    mockUserLogin = {
      login: function() {
        return {
          $promise: queryDeferred.promise
        };
      }
    }

    spyOn(mockUserLogin, 'login').and.callThrough();
    spyOn(scenarios, 'success').and.callThrough();
    spyOn(scenarios, 'fail').and.callThrough();
  }))


  it('runs `success scenario` if user object is fetched', function() {
    queryDeferred.resolve(mockLoginUser)

    userLogin(mockLoginUser, scenarios);

    expect(scenarios.success).toHaveBeenCalled()
    expect(scenarios.fail).not.toHaveBeenCalled()
  });

  it('runs `fail scenario` if user object is not fetched', function() {
    var reason = {
      error: 'some error'
    }
    queryDeferred.reject(reason)

    userLogin(mockLoginUser, scenarios)

    expect(scenarios.success).not.toHaveBeenCalled()
    expect(scenarios.fail).toHaveBeenCalledWith(reason)
  });

  function userLogin(mockLoginUser, scenarios) {
    mockUserLogin.login(mockLoginUser)
      .$promise.then(scenarios.success)
      .catch(scenarios.fail)
      .finally();

    $rootScope.$apply();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>

第二版 - 注入服务和$ httpBackend

  

当Angular应用程序需要来自服务器的某些数据时,它会调用$ http服务,该服务使用$ httpBackend服务将请求发送到真实服务器。使用依赖注入,很容易注入$ httpBackend mock(它具有与$ httpBackend相同的API)并使用它来验证请求并使用一些测试数据进行响应,而无需向真实服务器发送请求。

angular.module('flightlottery.userApi', ['ngResource']).
factory('User', function($resource, $rootScope) {

  var User = $resource('http://somesite.ca/api/users/:method/:id', {}, {
    login: {
      method: 'POST',
      params: {
        method: 'login'
      }
    }
  });

  User.loginUser = function(cb) {
    return User.login(cb);
  }

  return User;
});


describe("User Service Test", function() {

  var $httpBackend, User;

  beforeEach(module('flightlottery.userApi'));

  beforeEach(inject(function(_$httpBackend_, _User_) {
    $httpBackend = _$httpBackend_;
    User = _User_;
  }));
  
  afterEach(function() {
     $httpBackend.verifyNoOutstandingExpectation();
     $httpBackend.verifyNoOutstandingRequest();
   });

  it('calls `POST` method to interacts with backend', function() {
    var stubUser = {
      name: 'Some name',
      password: 'somePassword'
    };
    var stubResponse = {
      login: 'someName',
      lastLogin: Date.now()
    }
    
    spyOn(User, 'login').and.callThrough();
    
    User.loginUser(stubUser).$promise.then(function(response) { // response is stubbed by second argument of repond method
      expect(response.login).toBe(stubResponse.login)
      expect(response.lastLogin).toBe(stubResponse.lastLogin)
    });
    
    expect(User.login).toHaveBeenCalledWith(stubUser)
    $httpBackend.expectPOST('http://somesite.ca/api/users/login').respond(200, stubResponse)
    $httpBackend.flush();
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine.css" rel="stylesheet" />
<script src="//safjanowski.github.io/jasmine-jsfiddle-pack/pack/jasmine-2.0.3-concated.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-resource.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular-mocks.js"></script>