How to test 'private' functions in an angular service with Karma and Jasmine

paniclater picture paniclater · Jun 23, 2015 · Viewed 25.9k times · Source

I have a service in my angular app that looks something like this:

angular.module('BracketService', []).factory('BracketService', [function() {
    function compareByWeight(a, b) {
        return a.weight - b.weight;
    }
    function filterWeightGroup(competitors, lowWeight, highWeight) {
        //filter stuff
    }
    function createBracketsByWeightGroup(weightGroup) {
        //create some brackets
    }
    //set some base line values
    var SUPER_HEAVY_WEIGHT = 500;
    var SUPER_LIGHT_WEIGHT = 20;
    return {
        //create brackets from a list of competitors
        returnBrackets: function(competitors) {
            var brackets = {};
            //get super light weights
            brackets.superLightWeights = createBracketsByWeightGroup(
                filterWeightGroup(competitors, 0, SUPER_LIGHT_WEIGHT)
                .sort(compareByWeight)
            );
            brackets.superHeavyWeights = createBracketsByWeightGroup(
                filterWeightGroup(competitors, SUPER_HEAVY_WEIGHT, Infinity)
                .sort(compareByWeight)
            );
            brackets.middleWeights = createBracketsByWeightGroup(
                filterWeightGroup(competitors, SUPER_LIGHT_WEIGHT, SUPER_HEAVY_WEIGHT)
                .sort(compareByWeight)
            );
            return brackets;
        }
    };

}]);

I would like to unit test not just the functions / properties that are exposed in the return statement, but also the functions that are outside of the return statement.

My test is currently set up something like this:

describe('BracketService', function() {
    beforeEach(module('bracketManager'));

    it('calling return brackets with no competitors will return 3 empty weight classes', inject(function(BracketService) {
        var mockCompetitors = [];
        var mockBracketResult = {superHeavyWeights: [[]], superLightWeights: [[]], middleWeights: [[]]};
        expect(BracketService.returnBrackets(mockCompetitors)).toEqual(mockBracketResult);
    }));
});

But how do I test the compare, filter and createBrackets functions that are not exposed by the return statement?

Thanks!

Answer

Robert Moskal picture Robert Moskal · Jun 23, 2015

There is no way to test those functions. Their scope is the function that comprises your BracketService factory and they are invisible anyplace else. If you want to test them, then you have to expose them somehow.

You can move them into their own service (which seems like overkill) or you can black box test your BracketService service with enough data combinations to make sure the internal functions are working. That's probably the most sensible approach.

If you don't want to put them in a separate service, but still feel the need to test those internal functions, just return them from the factory along with returnBrackets.

I might do this when I have a number of helper functions that are straight forward to test individually, but open up a combinatorial Pandora's box to black box test. I usually preface such functions with an "_" to show they are helper functions and are only exposed for testing.

return {
    //create brackets from a list of competitors
    returnBrackets: function(competitors) {...},
    _filterWeightGroup: filterWeightGroup,
    _createBracketsByWeightGroup: createBracketsByWeightGroup
   };