Testing controllers - aca-mobile/ti-unit GitHub Wiki

The question arises: "Should we really test controllers?". This depends. How much logic do we want inside a controller? And if there is only 'glue' code inside a controller, why should we unit test them? Is there still a valid reason? I guess not, though it depends case by case.

But if we do, we need to keep following points in mind:

  • add a modules.exports.test block which exports the test functions

Example controller:

var UserManager = require('user.manager');
function _onAuthSuccess() {
    Alloy.Globals.loading.hide();
    UserManager.setUserName($.username.getValue());
    Alloy.createController('dealers').getView().open();
    $.authenticate.close();
}
function _onAuthLoginError() {
    Alloy.Globals.loading.hide();
    Alloy.Globals.notifications.showError(L('authenticate.error.authentication.failure'));
}
function _onAuthTokenValidationFailure() {
    Alloy.Globals.loading.hide();
}
module.exports.test: {
    _onAuthSuccess: _onAuthSuccess,
    _onAuthLoginError: _onAuthLoginError
}

It is of the utmost importance to export the test functions on modules.exports.test, otherwise these exports will cause the application to fail (due to how Alloy code is post-processed) upon (2nd) controller instantiation. Example controller test:

describe('authenticate test', function() {
 
    var USER_NAME = "John Doe";
 
    var userManagerMock = {
        setUserName: function(){},
        getUserName: function(){}
    };
 
    var dealerControllerMock = {
        getView: function(){}
    };
 
    var dealerViewMock = {
        open: function(){}
    }
    Ti = require('tiunit/jsca.api.parser').parseFromConfig();
 
    MockRequire = require('tiunit/mockrequire');
 
    Alloy = {
        CFG: {
            throttleTimeOut: 100
        },
        Globals: {
            loading: {
                hide: function(){}
            },
            vabfsNetworkClient: {
                hasAutoLogin: function(){}
            },
            notifications: {
                showError: function(){}
            }
        },
        createController: function(){}
    };
 
    var controllerUnderTest; // class under test

    L = function(s){
        return s;
    };
 
    beforeEach(function () {
        $ = require('tiunit/mockcontroller').createControllerMock('../app/controllers/authenticate');

        MockRequire.addMock('user.manager', userManagerMock);
        controllerUnderTest = require('../app/controllers/authenticate');
    });
 
    afterEach(function(){
        MockRequire.resetMocks();
    });
 
    it('should create dealers controller when authentication was succesful', function(){
        spyOn(userManagerMock, 'setUserName');
        spyOn(Alloy.Globals.loading, 'hide');
        spyOn(Alloy, 'createController').and.returnValue(dealerControllerMock);
        spyOn(dealerControllerMock, 'getView').and.returnValue(dealerViewMock);
        spyOn($.username, 'getValue').and.returnValue(USER_NAME);
        spyOn($.authenticate, 'close');
 
        controllerUnderTest.test._onAuthSuccess();
 
        expect(Alloy.Globals.loading.hide).toHaveBeenCalled();
        expect(userManagerMock.setUserName).toHaveBeenCalledWith(USER_NAME);
        expect(Alloy.createController).toHaveBeenCalledWith('dealers');
        expect(dealerControllerMock.getView).toHaveBeenCalled();
        expect($.authenticate.close).toHaveBeenCalled();
    });
 
    it('should show error message when a login error has occured', function(){
        spyOn(Alloy.Globals.loading, 'hide');
        spyOn(Alloy.Globals.notifications, 'showError');
 
        controllerUnderTest.test._onAuthLoginError();
 
        expect(Alloy.Globals.loading.hide).toHaveBeenCalled();
        expect(Alloy.Globals.notifications.showError).toHaveBeenCalledWith('authenticate.error.authentication.failure');
    });
 
});