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

26,642

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.

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.

Share:
26,642
paniclater
Author by

paniclater

Software developer, musician and home-brewer. Significant Java, Ruby, and HTML/CSS experience.

Updated on July 29, 2020

Comments

  • paniclater
    paniclater over 3 years

    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!