We can write perfectly functioning code, that is simple, readable and optimised. But when it comes to testing, it adds another dimension to your code and changes the way you would approach coding. Those who have developed in C#, you know why we need Interfaces and implementing it will change the way you approach writing your code.
The following issues are known pain points when it comes to testing ES6 modules:
- Named Exports
- Multiple Exports
We will use a Warehouse example, which uses Mocha, Sinon and Chai, to explore how we can rewrite the code to get them to pass the above mentioned pain points, and still have them function properly. Although the title mentions spying, the idea is that if you can Spy then you should be able to Stub and Mock.
Setting the Scene
The following snippet is our warehouseManager.js. You can increase the stock count of a particular stock and save it to a store via warehouseRepository.js.
The following snippet shows the implementation of the repository, which isn’t that exciting.
The SUT is updateStockCount(). When we run the following test we find that it actually fails. What would your wild guess be?
If it had anything to do with the import statement then you are pretty close. But lets ask the right question. What is Sinon spying on?
Sinon is spying on the variable updateStockCount that contains the exported function updateStockCount from warehouseManager.js.
Let’s analyse that statement a bit further. The function updateStockCount was exported into the variable called updateStockCount and Sinon is setup to spy on the variable rather than the actual function on that file. The following snippet behaves exactly the same, except for the fact that it uses an alias to export the function into. Hopefully this will clarify the issue a bit further.
Given we do not want to spy on a variable but rather that the a function. To do that we need to treat it like a module and import warehouseManager.js into as an object and then spy its functions. Given it is now being treated as an object, the Spy syntax changes a little bit. The following snippet shows how we can test it correctly.
The following test is trying to verify that when update the count of a stock that the getLastUpdateDate function was called.
Although the test reads well and should be passing, it unfortunately doesn’t.
The reason for it is because the getLastUpdateDate method that updateStockCount calls is in the local context and not the exported context.
What this means is that getLastUpdateDate is referring to updateStockCount as an internal function that outside world cannot inspect or know about Our test is spying on the the exported context, which is why we see that the call was never made. So how do we go about fixing this?
We can fix this by updating the warehouseManager.js to export a single module with multiple functions rather than exporting multiple functions.
This change isn’t going to pass the test immediately, actually it will be breaking the test. The test isn’t aware that warehouseManager.js has default export, so we need to modify the import to reflect this change.