Bruno D. Bruno D. - 2 months ago 27
AngularJS Question

AngularJS - Karma testing with providers and injections

I am having a lot of trouble to write the unit test case for a provider that contains some injections.

The particular provider is:

(function () {
angular
.module('core.router', [])
.provider('routerHelper', routerHelperProvider);

routerHelperProvider.$inject = ['$stateProvider', '$urlRouterProvider'];

/**
* This method Initializes the Router Helper provider to be used by all modules
*
* @param $stateProvider
* @param $urlRouterProvider
*/
function routerHelperProvider($stateProvider, $urlRouterProvider) {

this.$get = routerHelperService;

routerHelperService.$inject = ['$state'];

/**
* This method sets the methods to be used by the helper
*
* @param $state
* @returns {Object}
*/
function routerHelperService($state) {
var hasOtherwise = false;

return {
configureStates: configureStates,
getStates: getStates
};

/**
* This method configures all the states to be used by the application
*
* @param {!String[]} states
* @param {!String} otherwisePath
*/
function configureStates(states, otherwisePath) {
states.forEach(function (state) {
//console.log("adding state", state.state, "with config", state.config);
$stateProvider.state(state.state, state.config);
});
if (otherwisePath && !hasOtherwise) {
hasOtherwise = true;
$urlRouterProvider.otherwise(otherwisePath);
}
}

/**
* This method returns the states to be used by the application
*
* @return {Object}
*/
function getStates() {
return $state.get();
}
}
} })();


The basic unit test is:

'use strict';

describe('core.router test', function () {

// All Service injections
var $urlRouterProvider, $stateProvider;

// Mocks
var m_url = function () {
};
var m_state = function () {
};

// Others
var routerHelper, urlRouter, state, base;

// Before statements
beforeEach(module('core.router', function ($provide, _routerHelperProvider_) {
$provide.value('$urlRouterProvider', m_url);
$provide.value('$stateProvider', m_state);
base = _routerHelperProvider_;
}));

// Starting the Factory
beforeEach(inject(function (_routerHelper_, _$urlRouter_, _$state_) {
routerHelper = _routerHelper_;
urlRouter = _$urlRouter_;
state = _$state_;
}));

describe('when testing it', function () {
it('should return true', function () {

//var abc = routerHelper.getStates();

expect(1).toEqual(1);
});
});
});


I keep getting errors like:



  • Error: [$injector:unpr] Unknown Provider: $stateProvider

  • Error: [$injector:unpr] Unknown Provider: $urlRouterProvider

  • Error: [$injector:unpr] Unknown Provider: routerHelperProvider




I tried several different module instantiations and several different injections, but I can't seem to make it work. When I take out the injections (
$stateProvider
,
$urlRouterProvider
and
$state
), the unit test is straightforward.

Answer

So, this would be the solution, bringing some complexity because the provider is using both the $state and $stateProvider:

'use strict';

describe('core.router test', function () {

    // All Provider injections
    var $urlRouterProvider, $stateProvider;

    // Mocks
    var m_urlProvider = mockDataCore.urlRouterProvider();
    var m_stateProvider = mockDataCore.stateProvider();
    var m_state = mockDataCore.state();

    // Others
    var routerHelper, base;

    // Define the mock providers
    beforeEach(function(){
        module(function($provide){
            $provide.provider('$urlRouter', m_urlProvider);
            $provide.provider('$state', m_stateProvider);
        });
    });

    // Start the module with the internal mock
    beforeEach(function () {
        module('core.router', function ($provide) {
            $provide.value('$state', m_state);
        });
    });

    // Load the provider with module to be able to call its configuration methods
    beforeEach(function () {
        module(['routerHelperProvider', function (_$urlRouterProvider_, _$stateProvider_, _routerHelperProvider_) {
            $urlRouterProvider = _$urlRouterProvider_;
            $stateProvider = _$stateProvider_;
            base = _routerHelperProvider_;
        }]);
    });

    // Inject and start the provider
    beforeEach(function () {
        inject(['routerHelper', function (_routerHelper_, $state) {
            routerHelper = _routerHelper_;
        }]);
    });

    // test cases
    describe('when adding one state and no "otherwise"', function () {
        it('otherwise should not be called and state should be saved to state list', function () {

            spyOn(m_urlProvider, "otherwise");
            spyOn(m_stateProvider, "state");

            var simpleState = [{
                state : "home",
                config : {
                    url: "/home"
                }}];

            routerHelper.configureStates(simpleState);

            expect(m_urlProvider.otherwise).not.toHaveBeenCalled();
            expect(m_stateProvider.state).toHaveBeenCalledWith("home", {url: "/home"});
        });
    });

    describe('when getting the states', function () {
        it('should return the states', function () {

            spyOn(m_state, "get");

            var states = routerHelper.getStates();

            expect(m_state.get).toHaveBeenCalled();
        });
    });
});

The mock methods are:

var mockDataCore = (function () {
return {
    urlRouterProvider: urlRouterProvider,
    stateProvider: stateProvider,
    state: state
};

function urlRouterProvider() {
    return {
        otherwise: function () { /* void  */
        },
        $get: function () {  /* void  */
        }
    };
}

function stateProvider() {
    return {
        state: function () { /* void  */
        },
        $get: function () {  /* void  */
        }
    };
}

function state() {
    return {
        get: function () {
            return {};
        },
        go: function () {  /* void  */
        }
    };
}})();

Of course it doesn't cover all the tests for this provider, but the rest of them are pretty straight forward..