Testing Private functions through a Public Interface
The first thing you probably want to do is create a public interface for your library to expose your functionality publicly. An example of how this would look is given here:
In the example above, we have a closure that contains a public interface attached to window called myLib and a private object called Animal that does all the heavy lifting. MyLib is accessible publicly and has a public function selectAnimal that takes a parameter called code. All selectAnimal does is create an instance of Animal and returns the value of the Animal object’s name property which is set from a call to it’s getAnimal function with the specified code.
It is the getAnimal function that does all the work. It takes the code specified and through a series of conditions, sets the name property of Animal with the specific name of the animal.
Whereas myLib.selectAnimal is exposed publicly and can be tested easily. Animal is defined within the closure and is private and all it’s related properties and functions are also private, and thus inaccessible by any testing framework.
To ensure getAnimal is fully tested we have to use the public function to fully exercise every scenario that the private function would get called with.
An example of tests for this using the Jasmine Testing framework would be:
These tests would fully test both the public function selectAnimal and the private function getAnimal by mimicking every scenario for selectAnimal in which getAnimal may be called.
Hence when conceptualizing our tests, in true TDD style, conceptualize your tests for the private functions and view the public function as just a wrapper for your core implementation.
Though it might be tricky, depending on the level of complexity, to conceptualize tests for every scenario that would exercise both a public function and any potential private functions it may use, it is not impossible and if you are following detailed methods of conceptualizing your tests, this shouldn’t be difficult.
The same approach goes for standalone functions defined within the closure. For example, lets modify our code to let selectAnimal receive a JSON object, and we send that to a utility function defined within the closure:
Here we added a utility function extractCode that is a standalone function defined within the closure, and is thus also private. We use extractCode to extract the value for code from the JSON object received by selectAnimal. This example would not require any additional tests for selectAnimal to exercise extractCode as well since all extractCode does is return the code, but you get the general idea.
An Alternative Approach: Adding a Public Instance of the Private Object
Another approach would be to add a public instance of the private object to the public interface. So we could modify our solution to include a property of myLib that instantiates an instance of Animal. For example:
So here, all we do is make the selector variable a public property of window.myLib and use it to initialize an instance of the Animal object in our initialize function. So now when we call the getAnimal function, we use the this self referencer to access the public property like so: this.selector.getAnimal.
This now gives us an instance of Animal that is accessible publicly. We can use that in our tests to test the private object and its related functions directly. Example:
Now that we are able to test getAnimal independently, we can reduce our tests for selectAnimal, as shown.
Testing Callback functions without mocking asynchronous calls
Callback functions are commonly used in asynchronous calls such as AJAX requests. Though there are many third party mocking libraries that simulate asynchronous calls that can allow these functions to be tested, in an environment with little or no use of third party libraries, this can be a real challenge to test. Well now that we have our public instance of our private object, this becomes significantly simpler. Lets modify our code snippet to include an AJAX call using JQuery’s ajax function:
In the example above we modified our getAnimal function to include an AJAX call made to an endpoint getDescription.json that returns the appropriate description based on the code supplied. Upon a success response, a function is called that sets the name property of the Animal object.
Without the use of some third party library for mocking asynchronous calls, it would be near impossible to test that success function. However making a slight modification to the code can make this function more testable. Example:
So all we do here is make the callback function a method of the Animal object called getAnimalCallBackFn. So now whenever we create an instance of Animal, we will have access to the getAnimalCallBackFn independently of the ajax call.
Now we can use our public instance of Animal, selector, to test getAnimalCallBackFn. Example: