如何用服务编写控制器的单元测试

时间:2014-05-14 16:25:31

标签: javascript angularjs unit-testing jasmine

我在angularjs中学习单元测试,并试图测试我的身份验证控制器。

目前,测试失败了Expected Function to equal '/dashboard'。该测试似乎没有从我所知道的User.login进入。

控制器:

angular.module('foo').controller('LoginCtrl',function($scope, $rootScope, $http, $window, $location, User){

 $scope.submit = function () {

      User.login($scope.user,

          function (user) {    // Success
            $window.sessionStorage.token = user.id;
            $scope.message = 'Welcome';
            // Redirect to dashboard
            $location.path('/dashboard');
          },
          function (err) {
            console.log(err);

            // handle login errors
            $scope.message = 'Error: Invalid user or password';
          }
      );
    };
});

测试:

describe('LoginCtrl', function() {

    beforeEach(module('foo'));

    var scope, ctrl, location, window, user;

  beforeEach(inject(function($rootScope, $controller, $location, $window, User) {
    scope = $rootScope.$new();
    location = $location;
    window = $window;
    user = User;
    ctrl = $controller('LoginCtrl', {$scope: scope, User: user});
  }));

  it('should redirect upon successful login', function() {
    console.log('before path = '+location.path());
    scope.user = {
      "username": "my_user",
      "password": "my_pass"
    };
    scope.submit();
    console.log('message = '+scope.message);
    console.log('after path = '+location.path());
    console.log(window.sessionStorage.getItem('token'));
    expect(location.path).toEqual('/dashboard');
  });

});

**编辑**

User.login代码:

module.factory(
  "User",
  ['LoopBackResource', 'LoopBackAuth', '$injector', function(Resource, LoopBackAuth, $injector) {
    var R = Resource(
      urlBase + "/users/:id",
      { 'id': '@id' },
      {

      "login": {
          url: urlBase + "/users/login",
          method: "POST",
          interceptor: {
            response: function(response) {
              var accessToken = response.data;
              LoopBackAuth.currentUserId = accessToken.userId;
              LoopBackAuth.accessTokenId = accessToken.id;
              LoopBackAuth.rememberMe = response.config.params.rememberMe !== false;
              LoopBackAuth.save();
              return response.resource;
            }
          }
        }
});

2 个答案:

答案 0 :(得分:2)

  

预期功能等于'/ dashboard'

测试运行器告诉你它需要一个字符串'/dashboard',而是获得一个函数的引用。那是因为location.path 是对函数的引用。试试这个:

expect(location.path()).toEqual('/dashboard');

答案 1 :(得分:2)

您的User.login函数必须调用回调方法异步,因此当您致电scope.submit();时,您的回调函数尚未调用 = >测试失败。

要测试此逻辑,您必须模拟User.login函数:

 it('should redirect upon successful login', function() {
    console.log('before path = '+location.path());
    scope.user = {
      "username": "my_user",
      "password": "my_pass"
    };

    //user here is user = User; in your beforeEach. I avoid pasting too much code.
    //Jasmine 1.3: andCallFake
    //Jasmine 2.0: and.callFake
    spyOn(user, "login").andCallFake(function(userData,successCallback){
         successCallback(userData); //simulate the success case.
    }); //mock your User.login

    scope.submit();
    console.log('message = '+scope.message);
    console.log('after path = '+location.path());
    console.log(window.sessionStorage.getItem('token'));
    expect(location.path()).toEqual('/dashboard'); //fix the problem with location.path
 });

<强>解释

spyOn(user, "login").andCallFake用我们的假函数替换实际函数。

在此测试用例中,您正在测试should redirect upon successful login,因此前提是登录必须成功,通过模拟登录功能,我们可以确保此前提条件为在测试中总是为真。

您可以类似地测试类似于set error message when login failed的案例,为了对此进行测试,您需要确保测试中的前置条件login failed 始终为true :

it('should redirect upon successful login', function() {
    console.log('before path = '+location.path());
    scope.user = {
      "username": "my_user",
      "password": "my_pass"
    };

    //user here is user = User; in your beforeEach. I avoid pasting too much code.
    //Jasmine 1.3: andCallFake
    //Jasmine 2.0: and.callFake
    spyOn(user, "login").andCallFake(function(userData,successCallback,errorCallback){
         errorCallback(userData); //simulate the error case.
    }); //mock your User.login

    scope.submit();

    expect(scope.message).toEqual('Error: Invalid user or password'); //verify that the message was set correctly.
 });