Unit tests are something we all heard of but not everyone had oportunity to see them at work ;)
In different languages unit testing can differ due to variaty of testing framework and the capapilities of the language.
For example when I start reading about using stub structures in C++ [1] my brain is lagging
Thankfully this post is about JavaScript and testing it is really easy
Sinon.js/Typescript/Mocha
In this post I'll focus on unit tests made with Mocha[2] and library SinonJS[3] for Typescript environment
Mocha
Mocha is a test framework which will invoke our tests. It was written to execute tests written in JavaScript but when you look into their github [5] you will find plenty of configurations
In my case I used the basic configuration for typescript environment [6]
SinonJS
SinonJS is a library that allows you to make stubs/spies and mocks in JavaScript. On their webiste it is claimed that SinonJS Works with any unit testing framework which is true since it should not interfere with testing framework
As JavaScript object can be extendable via their prototypes something similar is happening with Sinon
To show what’s going on under the hood we need simple example
As you can see Before stubfetchData had only three properties and After stub output shows that fetchData was enchanced with Sinon so now it has whole familiy of methods ready to use by library
At the end we perform two tests that don't fail on the output. That means first call of fetchData was properly changed by Sinon and the second assert also pass which means the wrappedMethod property of fetchData has the orginal function call
Everytime test finish you need to restore previous object state. If you won't do this element that you stub will have this same stub in every test case.
There are different ways to restore Sinon stub but in this post I'll invoke sinon.restore after each test
afterEach(() => {
sinon.restore();
});
Simple stubs
First example that I'll show will stub api call that returns a promise
As you can see first test took 1.5s to finish. That means we queried our simulated api function which had timeout of 1500 miliseconds
Second one triggers promise rejection to show that this should throw Error and custom error message should be returned from catch (error) block
The third test was the one which Stub the api call with cutom response. In this case it was a different string
The last example was a bit more advanced in orter to show that you can not only modify function returns but it is also possible to trigger custom errors
Testing more complex code
Now let's see a more advanced function calls
So I made simple function call flow. Whenever first respose is returning 'First fetch responded: CrystalFightersGigShouldHappenInPoland' the secondFetch is called
In case of firstFetch, secondFetch is rejected or the error thrown, the catch (error) adds custom message
Unit tests are grouped and first group is the one which triggers real api calls. Second group is the Stub one
As you can see depending on the different stubs various flows of apiFetch were executed
At the end let's look at the Mocha output. What you can see is that Not stubbed api calls take a lot of time since they simulate remote resource that needs to be queried
apiFetch stub fistFech, secondFetch on the other hand is using Sinon and it takes only 500ms to execute. It's 3 times faster and more tests were executed in this same time
Checking execution path
Last example will be checking execution path. Imagine you have a function with specyfic algorithm and you want to be sure that this function will be called with certain arguments or your it will invoke other functions particular amount of times
This can be checked in your unit tests and it's pretty simple to do so
Code that we test don't change from example 3. Only thing that is different are the tests
As you can see with usage of ok function it was possible to check boolean properties of stubs
Not only it's possible to check how many times api call was invoked but we can check what arguments were provided
Conclusion
As you can see it's easy to stub remote api calls with custom responses. Also it is possible to check amount of function calls and arguments passed
This not only makes your code isolated but also no aditional remote dependencies are needed to test your code. It also gives you possiblity to run your tests when you are offline
For me the biggest gain from the unit tests is over time when lots of developers make changes to your code and with all this checks you can be sure that functionality that was build still works correctly
This might not ensure that some remote API will work when you deploy your code to production but at least you will be sure that certain triggers are toggled when for example API returns unwanted data
If you want to read more about stub/mock/spy in Sinon I suggest to use this blog post. [4] It's one of the best I found and I think even documentation of Sinon is not as good as this guys explanation.
Never test your code with connection to remote dependencies. This not only makes your tests slow but also you won't be able to reproduce same environment every time tests are run.
Try to write unit tests from the begining of the project and if you have legacy code that is not tested don't try to write all tests at once. This work should be splited and be incremental.