Integration & Unit Testing For Node.js
A Beginner's guide to testing Node & Express projects using Mocha & Chai ☕
Apr 10, 2021Software testing verifies that our code works as intended and meets the technical, functional, and user requirements that it needs to. Testing is a vital part of developing any software, but is so often overlooked by new engineers as an area to learn and develop their skills. It doesn't help that more often than not, it's hard to find good tutorials, guides or blog posts explaining the different kinds of tests for a code project, and how to actually write these tests.
To begin with, I wanted to go over some important words that pop up all the time around software testing, and what they mean:
Unit Testing - Unit testing is the testing of the smallest 'units' or components of a program. In most cases, the smallest unit under test would be a function. Therefore, in these situations, we are testing to make sure our individual functions work as expected.
Unit testing is performed before any other type of software testing, and it is usually the responsibility of the developer to conduct & write these tests.
Integration Testing - Integration testing is the testing of a combination of a related group of units of code that provide a given functionality. In this case, we are testing for bugs that can occur within the interactions between units.
Integration testing can also be the responsibility of a developer, but it's not uncommon to have independent testers to also get involved with integration testing.
In this example, we will go over unit and integration tests for a Node.Js & Express API project. The main functionality of our example project will be responding to GET HTTP requests with a random number, this isn't too important, as the main focus will be on the tests.
Tools
Node.js & Express 🔥
We are going to be creating a simple Node.js application that uses the Express.js framework to create a mini-API. This is just for example purposes, and will act as the code under test.
For more information on both of these, please have a look at the Node.js and Express.js documentation.
Mocha ☕️
Mocha is a flexible, open-source JavaScript testing framework that runs on Node.js. It works for testing synchronous and asynchronous code, and nicely logs the test results to the console. Mocha can be used for both unit & integration testing.
Mocha can be used alongside most assertion libraries (a tool that is in charge of the verification of your test results), but the most popular Node.js assertion library used with Mocha is called Chai.
Chai & Chai-http 🫖
Chai is a Behaviour / Test driven design assertion library used frequently with Mocha in Node.js testing. Chai-http is a plugin for Chai that allows us to write HTTP integration tests with Chai.
The hot-beverage themed pair work together to create a great way to get started with testing our Node.js & Express projects.
The Set Up
Requirements: Node.js installed.
In order to best demonstrate testing, we are going to need some code to test.
(If you're using this tutorial as a guide to testing an existing project - great! You don't need to create this project, but the testing should follow the same principles.)
First things first, create a new Node.js project (create a new folder and run npm init
from there). After this, go ahead and install express using npm install express
.
Next, we will create our server.js
file, which is going to hold all the logic for our example app:
Our Node app listens at localhost:3000
and exposes an endpoint /random
that returns a random number. You can give it a try by running your app (npm start
), and navigating to localhost:3000
in your browser.
The Tests
Now its time to test!
To set this up correctly, I'd advise that you create a test
sub-folder to keep all your testing files for this app together.
Next up, we need to install our testing libraries by running:
npm install mocha chai chai-http
And making sure that in our package.json
we have added Mocha to our test script like so:
...
"main": "server.js",
"scripts": {
"start": "node server.js",
"test": "mocha"
},
"author": "",
...
This means that now we only need to run npm test
when we want to run our tests. We are now all set up and ready to write our first unit test.
Unit Tests
As mentioned earlier, unit testing is all about testing single, individual functions. In the case of our example code, we are going to write a unit test that checks that our getRandom
function does what it's supposed to and generates a random whole number between 0 and 100.
We first have to require chai and chai.assert to get our assertion library. Then we describe the function under test, call that function, and make assertions on the result.
Here, the describe
function encapsulates our test. The first argument is a string that describes what we are testing (for the sake of example, I have called it 'Unit Test', but it would be more descriptive to pass something like 'GetRandom random number generation').
The second argument passed is a function that acts as the body of the test. It starts with an it
function which, similar to the describe
function, describes the test. Here, we give a small description of the functionality we are testing, or what the expected functionality is. The second argument is another function, and in this function we can actually write our test code.
In this example, we are using the assert
assertion style, but Chai offers other styles of assertion, such as should
or expect
. You can find out the differences between them in the Chai Assertion Styles Guide.
When this test is run, our getRandom
function is called, and the test makes assertions on the result. We assert (verify) that the returned result (random
) is not null, is a number, and is at least 0, but no more than 100. If all these assertions pass, that means our code is functioning as it is supposed to, and our unit passed it's unit test.
Integration Tests
Testing the functionality of our API endpoint constitutes an integration test as we are testing how all the individual parts of our program work together to send a response back when our /random
endpoint is hit. In order to do this, we need to use chai-http
. This package allows us to make HTTP requests to our server during tests.
Because of the use of asynchronous code and promises in the implementation of this functionality, we have to approach the integration test for this code slightly differently. We pass a callback inside the it
function that calls and tests our asynchronous code (same as before).
chai-http
allows us to construct a request to our application by passing in our app. This automatically opens our server for incoming requests. We can then use .get()
to call our /random
endpoint. The end
method is then used to make the request and assert on the response.
Because the end
functions is a callback, we have to use the done
callback in order to indicate to Mocha that our callback has completed and the assertions are ready to be verified. If we don't do this, the test will pass before the assertions are checked.
We check that our response contains a successful status code, and that the number that the response returns is not null, is a number, and is at least 0 but no bigger than 100 (no change there).
In this integration test, we are making sure that not only are we provided with the response from the API endpoint that we are expecting, but we are also making sure that our API as a whole behaves as expected when responding to a GET request. This is testing how multiple 'units' of our application come together to provide this functionality.
And that's it! We have completed a unit and an integration test for our mini-API.
While this is a simplified example, you can use these as a template to go on and test your own, more in-depth Node & Express projects. The implementation remains the same, but you can develop these example tests further and duplicate them in order to test larger and more complex programs.
Below I have linked to some other great guides on unit & integration testing in Node.js, as well as the official documentation for Mocha & Chai. Happy testing!
Resources
- Unit Testing in Node.js with Mocha & Chai - https://livecodestream.dev/post/testing-in-nodejs-using-mocha-and-chai-part-1/
- Integration Testing in Node.js with Mocha & Chai - https://livecodestream.dev/post/testing-in-nodejs-using-mocha-and-chai-part-2/
- JavaScript Testing with Mocha & Chai - https://codeburst.io/javascript-unit-testing-using-mocha-and-chai-1d97d9f18e71
- Unit & Integration Testing for Node.js - https://blog.logrocket.com/unit-and-integration-testing-for-node-js-apps/
- Mocha - https://mochajs.org
- Chai - https://www.chaijs.com
- Chai-http - https://www.chaijs.com/plugins/chai-http/