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

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

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);
}));
});

Related:  Multiple files on CDN vs. one file locally

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

Solutions/Answers:

Solution 1:

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
   };

Solution 2:

You will not be able to call those functions without exposing them somehow. But, IMHO, private methods should not have a unit test perse, but be tested at the time the public method that calls them is tested. What you should do is mock the objects that your private function will receive and you will be able to perform expectations on them.

Related:  JavaScript equivalent of jQuery's extend method

Solution 3:

The only way to test them in your current setup is to test the returned function since they’re currently local to the scope inside the BracketService. If you want them to be individually testable, you’ll need to expose them in the return statement as properties of BracketService.

References