Search code examples
angularjsunit-testingkarma-jasminekarma-mocha

Angular: Mock a Login with $auth


I'm trying to work out how to unit test my login controller with Karma/Jasmine/Mocha.

I basically want to test if a 200 comes back from the $auth.login() then the message saved should be equal to "successfully logged in", otherwise if I receive a 401 then the message that comes back should be "error logging in".

UPDATE

This is where I'm at, at the moment.

login.controller.js

function loginCtrl($auth, $scope, $rootScope, $location) {
    var vm = this;

    vm.login = function() {
        var credentials = { email: vm.email, password: vm.password };
        // Use Satellizer's $auth service to login
        $auth.login(credentials).then(function() {
          vm.message = "Successfully logged in!";
        }, function(error) {
          vm.message = "Error logging in!";
        }).then(function(responses) {
          $location.path('home');
        });
    };
}

login.controller.spec.js

describe('Login Controller', function() {
  var q, scope, ctrl, auth;

  beforeEach(module('app.login'));

  beforeEach(inject(function($q, $rootScope, $controller, $auth) {
    q = $q;
    scope = $rootScope.$new();    
    ctrl = $controller('loginCtrl', { $scope: scope, SessionService: sessionService, $auth: auth, $q: q });
    auth = $auth;
  }));

  it('should present a successfull message when logged in', function () {
    var defer = q.defer();
    sinon.stub(auth, 'login')
    .withArgs({ email: 'test@test.com', password: 'test_password' })
    .returns(defer.promise);

    ctrl.login();
    defer.resolve();
    scope.$apply();
    expect(ctrl.message).to.equal('Successfully logged in!');
  });
});

Solution

  • Since this is your controller test, you'd probably need to spyOn your service ($auth) like so (in Jasmine) -

    var defer = $q.defer();
    spyOn('$auth', login).andReturn(defer.promise);
    controller.email = 'test@test.com';
    controller.password = 'test_password';
    
    controller.login();
    defer.resolve();
    scope.$apply();
    
    expect($auth.login).toHaveBeenCalledWith({ email: 'test@test.com', password: 'test_password' });
    expect(scope.message).toEqual("successfully logged in");
    

    and for the failure case using defer.reject() and pretty much the same format for assertion.

    In my opinion, you'd end up worrying about http related status-codes or responses only at the service level and not at the controller level. There you'd use $httpBackend to mock the responses with their status-codes and the corresponding responses.

    EDIT

    In mocha, as per my research, you'd end up doing something like -

    sinon.stub($auth, 'login')
    .withArgs({ email: 'test@test.com', password: 'test_password' })
    .returns(defer.promise);
    

    to stub the method. And the verification of the call as -

    sinon.assert.calledOnce($auth.login);
    

    Rest of it remains the same. The assertion of the message will also change to assert.equal for mocha.

    EDIT

    Checkout this fiddle - http://jsfiddle.net/9bLqh5zc/. It uses 'sinon' for the spy and 'chai' for assertion