Setting up Node JS project for testing with Jest

Salitha Chathuranga
5 min readJan 29, 2023

--

Let’s test something!

Hi guys! After a long break, thought of writing Node JS related article which could be very useful. Normally, most of developers are into TDD approach these days(Test Driven Development). Do you know why it is so important? Simply, tests can verify our functionalities are working well.

Let’s say we change some function behavior. Then we have to verify the expectations. So, we can simply run the tests written for that function and make sure still our code works fine. If it is failing, we have to have a look there. 😎

In Java Script world, there are so many test frameworks. Some of them are Jest, Mocha, Chai, Enzyme and etc. Personally I prefer Jest for backend tests since it has a bunch of features as a fully fledged framework.

So, why to use Jest?

  • Fast and simple to use
  • Configurations are simple: https://jestjs.io/docs/configuration
  • Mocking capability including spies and mocks
  • Coverage reports
  • Assertions with different ways
  • Promise support
  • Open source

Without more talks. let’s start! I will show you how to setup an Express JS project to be run with tests using Jest.

Project Structure 💥

Basically, we have package.json in the root level. Then I personally follow the below structure which is very easy for maintenance.

|
- src
|
- test
| - unit
| - integration
|- package.json
|

Under “test” folder, I will segregate the tests accordingly. Later I will create some jest configurations in the root level for test scripts.

Dependencies 💥

There is a set of core NPM dependencies that I use for express projects having tests. All these are dev dependencies. First 2 of dependencies are related to Jest framework. Nodemon is for running express app on live reload mode. Next one — npm-run-all is used to combine multiple commands and execute together. For test coverage related commands, we need nyc NPM package.

"devDependencies": {
"@jest/globals": "^29.3.1",
"jest": "^29.3.1",
"nodemon": "^2.0.20",
"npm-run-all": "^4.1.5",
"nyc": "^15.1.0"
}

Let’s create configurations…

Jest Configurations 💥

You may have seen when we create a Node JS project using npm init command, we get the prompt to give script for test. This is the point we should place our test command usually.

In real world, we have several types of tests like unit, integration, functional and etc. In this case I use separate config for each type of test. For now I will show you how to setup these for unit and integration tests.

Common Config

Naming convention: jest.config.js

This is the common config that can be used to inherit as the common setup for each test config. We can override the below properties according to our test setup.

As an example, here testMatch is the config that we should give the path for the files that we should consider for that test type. Since this is the common config, I have kept it as an empty array. In unit test config, I will override this providing the unit test file path.

module.exports = {
testEnvironment: 'node',
collectCoverage: true,
coverageDirectory: 'coverage',
testMatch: [],
transformIgnorePatterns: [
'[/\\\\\\\\]node_modules[/\\\\\\\\].+\\\\.(js|ts)$'
],
transform: {},
coveragePathIgnorePatterns: [
"/node_modules/"
]
};

You can add more configurations following Jest guide here: https://jestjs.io/docs/configuration

UNIT Testing Config

Naming convention: jest.config.unit.js

const commonConfig = require('./jest.config');

module.exports = {
...commonConfig,
testMatch: [
'**/*.unit.test.{js,ts}'
],
collectCoverageFrom: [
'**/modules/**/*.js',
'**/src/clients/*.js',
'**/src/services/*.js',
'**/src/common/utils.js'
],
coverageDirectory: 'coverage/unit'
};

So, you can see how common config is imported and overridden. Among these configurations collectCoverageFrom is the place where we should place the file paths which should be taken into consideration while generating coverage for the tests. And coverageDirectory is the path where the unit test coverage report is generated.

INTEGRATION Testing Config

Naming convention: jest.config.integration.js

const commonConfig = require('./jest.config');

module.exports = {
...commonConfig,
testMatch: ['**/*.integration.test.{js,jsx,ts,tsx}'],
coverageDirectory: 'coverage/integration'
};

This is the config for integration tests. I have removed collectCoverageFrom config because in integration tests, so many files are affected rather than unit tests. Likewise we can add/remove any configuration we want.

Let’s see how to integrate these configurations as a PRO! 😎

Test Scripts 💥

This is the most important part of this article!!!

So far, we created 2️⃣ configs…right? Now we are going to integrate them into NPM commands for tests. This is the time to modify our package.json file.

"scripts": {
"test": "npm-run-all --aggregate-output cov:clean -p cov:unit cov:integration -s cov:summary-preset cov:report",
"test:integration": "npm-run-all cov:clean cov:integration",
"test:unit": "npm-run-all cov:clean cov:unit",
"cov:clean": "rimraf .nyc_output && rimraf coverage",
"cov:unit": "jest --forceExit --colors -c jest.config.unit.js",
"cov:integration": "jest -runInBand --forceExit --colors -c jest.config.integration.js",
"cov:summary-preset": "mkdir .nyc_output && cp coverage/unit/coverage-final.json .nyc_output/unit.json && cp coverage/integration/coverage-final.json .nyc_output/integration.json",
"cov:report": "nyc report --reporter html --reporter lcov --report-dir ./coverage/summary"
}

I have 3 test commands here. Overall test command and commands for 2️⃣ types of tests. Let me brief about them…

Usually coverage report is generated inside project root level.

cov:clean This will run before tests to remove old coverage folder and outputs.

cov:unit Jest command to run unit tests. You can see how I have linked the previously created config here. 😎 Test results will be created inside coverage/unit folder since we have mentioned it in jest config.

cove:integration Jest command to run integration tests. Config file is linked here also. Test results will be created inside coverage/integration folder.

cov:summary-preset The command we need to create final output JSON files in each test folder. This will create a folder called .nyc_output folder and move the JSON outputs into each test type folder.

cove:report This is the command to create HTML test coverage report using nyc library. The summary of the results will be located in this place — /coverage/summary.

These are the sub commands used in test commands. Let’s move onto test commands. With the help of npm-run-all we can combine the scripts we created already.

test:unit This script will clean the coverage and run unit tests.

test:integration This script will clean the coverage and run integration tests.

Finally we have the main test script where we run all tests together and generate results!

test : This will do clean coverage, run unit tests, run integration tests, generate HTML report tasks altogether.

This kind of result will be there when you run overall test command.

This summary folder can be later sent to SonarQube to show the test coverage for your project.

Now, test setup is complete! ❤️ Next part is writing tests for your code! Use this setup as a boilerplate for setting up Jest for your Node JS project. I’m pretty sure, this will make our TDD much easier.

--

--

Salitha Chathuranga

Associate Technical Lead at Sysco LABS | Senior Java Developer | Blogger