angularjs - Angular 和 Karma 测试使用 $resource 并返回 promise 的服务

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

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

'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;
});

最佳答案

在尝试猜测您想要完成什么之后,进行少量重构 - 结果如下所示。

我总是想知道我们真正想要测试什么以及测试通过或失败时我们想要证明什么。

带有依赖项的版本

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>

第二个版本 - 注入(inject)服务和 $httpBackend

When an Angular application needs some data from a server, it calls the $http service, which sends the request to a real server using $httpBackend service. With dependency injection, it is easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify the requests and respond with some testing data without sending a request to a real server.

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>

关于angularjs - Angular 和 Karma 测试使用 $resource 并返回 promise 的服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31464182/

相关文章:

iOS 测试和 UITests 混淆

c# - 安装 Visual Studio 11 beta 后,不再通过 Visual Studio 2010 执行测试

php - 测试 PHP_SAPI! ='cli' phpunit 代码

javascript - 使用 AngularJS 检测多个选择/下拉菜单的相同项目选择

javascript - 单击绑定(bind)指令更新 Controller $scope 后是否需要调用 Angular 的 $apply?

angularjs - 从 Angular 表达式访问窗口

javascript - 动画每个 ng-repeat 迭代

angularjs - 具有多个 Controller 的 Angular JS 测试驱动开发

unit-testing - 应用测试返回 "NullInjectorError: No provider for Location!"

unit-testing - 使用 DI 进行单元测试和模拟服务