Mocha , Chai and Sinon with ES6 syntax

shishir kaji shrestha
6 min readMar 14, 2021

GitHub repo : https://github.com/shishirkaji/nodejs-testing-sinon

Sunny, nice, relaxed public holiday Monday to write a blog about an API that gives us quotes depending upon the number we send to the API. This blog will explain how we can use Sinon library to mock functions to create unit tests. The article is complimented with a full functioning source code which uses es6 syntax to demonstrate unit testing using Mocha , Chai and Sinon. We will go through the testing part together in this article.

Unit test is very important when it comes to designing, developing and maintaining medium to large scale application. At least in my experience. It helps code become resilient to changes and make us aware of side effects of change in the code. For instance you have a 100 file application and among them lies a function named ‘A’ that uses another function named ‘B’. Making changes to function B may break A and who knows where else the function B may have been used.

In your command line interface type npm install --global mocha to install mocha, then `npm install --save-dev chai sinon @babel/register @babel/polyfill The babel register and polyfill will be used to transpile es6 to a syntax that can be understood by NodeJS. Why don’t we just write what is understood by NodeJs ? Good question, es6 was released in 2015 which has new updates to es5 mainly syntactical sugars and heaps of other cool features. Most modern web browser can understand es6. However, NodeJs does not support ES6 yet. So we might want our front end and back end code to be uniform.

Lets Add test script to package.json

"scripts": {"start": "nodemon --exec babel-node ./server/app.js","test": "mocha --require @babel/polyfill --require @babel/register server/**/__test__/*.test.js"},

The script basically instructs NodeJS to use mocha and require simply means to require @babel/polyfill and @babel/register . Hence mocha uses these two to understand our es6 codes. Then finally we are using wildcard to command mocha to look for any files in server. This is how mocha would look at it.

server/ look for all the __test__ folder in server directory once __test__ folder is found look for any files with anything.test.js . Easy ? lets move on.

getAllQuotes.service.test.js

Now that we have our libraries, we are good to go. First lets take a look at server/services/quotes/quotes.service.js . Very important to remember that it is our responsibility to write codes that are easily testable. In this file we are send exporting an Object. The object being exported has a property called getAllQuote and the property has a function that is defined just above. The file only has one function. We will test this one.

The folder structure I have used is kind of convention and a bit of common sense :) . In the __ test__ folder I have added a file with same name as the one we are testing along with .test.js at the end. This bit ‘.test.js’ is what is picked up by mocha and will be run.

“describe” and ‘‘it’’ block used in this module is self explanatory. You can nest describe with in a describe block to show hierarchy like effect. In this example , “QuoteService” is the service we are going to test and “getAllQuote” is the function that is particularly being tested. Then comes the “it” function where we describe what functionality of the function is being tested.

We will see what are the dependencies of that function. We can clearly see that getAllQuote function calls axios.get() method and this is the part we will need to mock. The reason being , that we do not want our test to actually make a http GET request to the url. However, we do want to know that axios.get is being called. How do we do it ?

Sinon does the job. Initially I thought sinon was not for me but as I got to understand this bad boy, now I think it is very powerful and lets you do magic when unit testing. Sinon will let you check if axios.get() was called with particular set of parameter and will fail the test if not called. I will try my best to explain you what really happens here. Aye lets get going.

Lets begin by how axios work as it is important to understand Sinon’s effect. Once we understand this , we will understand more than just Sinon. if you go to node modules and see the axios/index.js , the module is exporting an instance of Axios with a default config and all its method such as get, put , post etc . And through out the whole program the same instance is used. When we restart or start a server , axios/index module in the node module folder exports an instance of axios that has all the methods we need to make a http requests.

Lets get back to the test file. we will now import the axios and then use below syntax to stub the axios.get() method.

const fakeUrl = "https://service.somefakeurl.com";      
const fakeResponse = { data: "some fake data" };
const axiosGetStub = sinon.stub(axios, "get"); axiosGetStub.withArgs(fakeUrl).resolves(fakeResponse);

In the above code we are creating a stub for axios.get(). After we stub the “get” method , we can get axios.get to do any thing we like, “LITERALLY”. You can get it to reject(error) , resolve(return promise), return (any value/object ). You have the power to change its behaviour. This makes the test absolutely independent of other libraries or functions and we need this independence to perform unit test. Look up what else Sinon can do in the documentation.

const response = await getAllQuote(fakeUrl);sinon.assert.calledOnceWithExactly(axiosGetStub, fakeUrl);assert.deepEqual(fakeResponse.data, response);

In the above code , we will use the function we are testing(getAllQuote) and then assert it to match expected outcome. When we call getAllQuote() function, it calls the axios.get(fakeurl).

At this point the axios.get() is never executed because we have stubbed it to give us required fakeResponse . Then we check if the stub was called once with fakeUrl as parameter. Boom done. We also deep equal the response value.

Testing a function that uses functions in same module.

Remember how we stubbed axios in the above example ? Now we will stub a function that exists in our module and we have not imported it. When I had to do this for the first time , I refactored heaps of code to facilitate testing. This cost me some time. Hence, I believe unit testing also determines the type of code we write as we need to write testable codes.

Lets checkout the server/services/blessings folder. In the blessings.service.js we have an object called internalFunctions and it has two methods named _calculateBlessings and _getRandomNumber . These two methods will be stubbed in order to test getBlessings function.

We will have to understand something here.

The blessings.service.js module returns an object that contains getBlessings and internalFunctions property. Now in the blessings.service.test.js file we import the two the properties as shown below.

Here we have to understand that the blessings.service.js has exported getBlessings function that always uses the same internalFunctions object that too has been exported. Here is where the scope comes into play. getBlessings and internalFunctions are in same scope.

If we do not keep an internal function inside an object as shown in example in image below, we will not be able to stub it. The reason is that every time dependentFunction is called it calls the someFunction which is in different scope unlike the internalFunctions.

What are your thoughts on this ? please Leave some constructive comments.

--

--