Node.js Unit Testing with Jest: A Quick Guide
Tags: node, testing, beginners
Published: 2023-10-27 02:06:42


**Unit testing** is a part of automated testing that involves testing the smallest part of your application called units. It helps find bugs faster and fix bugs early on in development.

Unit testing is like a coach carefully assessing a football team during practice. Unit testing examines the smallest parts of an application, much like the coach evaluates each player's skills according to their positions. Coaches evaluate players on man marking, alertness, tackling, and shooting range; attackers are evaluated on positioning, dribbling, and one-on-one play. Similarly, unit testing checks that each "unit" in the application functions as intended by testing specific abilities or functionalities. This comparison highlights the accuracy and detail of unit testing in the context of software development. 

**Getting Started with unit testing node.js**

After understanding what unit testing is and why it is vital. Let's talk about how to get started with unit testing. 

**Let's build a simple app.**

We'll make a simple task manager to store and retrieve tasks. Because this would be a simple program, we will store the data in an array. You'll use the command line to create a new directory, navigate to it, and launch a new app:


```
mkdir task-manager
cd task-manager
```
Open the app in your preferred code editor. Navigate to your terminal and run the command.
```
 npm init i
```
This is to install all your node_modules. 

Run the next command.
```
npm init -y
```
This is for your package.json file. Open your package.json and you will see the following code.

```

{
  "dependencies": {
    "init": "^0.1.2"
  },
  "name": "task-manager",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": ""
}

```

**Let's build our app**

A task manager app was chosen because many developers must have built a simple to do list or a complex task manager as a project. Because we are performing small and simple unit testing, we will build a relatively simple and small app.


Let's proceed. Create a file named taskManager.js in your project directory. This module will include functions for managing tasks. Paste the following code.

```
// task-manager.js
const tasks = [];

function addTask(task) {
  tasks.push(
    { 
        task, 
        completed: false 
    }
    );
}

function listTasks() {
  return tasks;
}

function completeTask(index) {
  if (index >= 0 && index < tasks.length) {
    tasks[index].completed = true;
  }
}

module.exports = { addTask, listTasks, completeTask };
```

The code above performs three functions. The first function is to add a task to the array of tasks. The tasks array will accept an object with two values: one for the task and another to indicate whether or not the task has been completed. The second function returns a list of all tasks that have been added, and the last function sets a task to completed using the task's index value. 



Lets a create a simple script to execute the functions and see if our app works using the command line. 

Create another file called app.js and paste the following code. 

```
// app.js

const taskManager = require('./task-manager')

taskManager.addTask('Buy groceries');
taskManager.addTask('Finish assignment');
taskManager.addTask('Go to schoolnode');
taskManager.completeTask(0)
taskManager.completeTask(1)
taskManager.listTasks().forEach((task, index) => {
  console.log(`Task ${index + 1}: ${task.task} (Completed: ${task.completed})`);
});
```
Run the following command to see your output

```
node app.js
```

**Getting started with unit testing. Install a test framework**

In order to get started with unit testing we are going to install a unit testing framework. There are different unit testing framework out there but for today's tutorial we are going to use jest. 

**What is Jest**
Jest is an open source  JavaScript testing framework designed to ensure correctness of any JavaScript codebase. Jest is designed to make simple and fun. Some features of jest include:

- Good documentation

- Feature rich-API that gives result quickly

- Simple to use

- Little to no configuration and  can be extended to match your requirements

- Fast and safe. 

Jest has  two commonly used  methods in a test file, which include:

1) describe(): Groups related tests together. It helps to structure your test and add context to them. The method takes in two arguments: a string describing the test suite (the suite name) and a callback function that contains one or more test or it functions. For example look at the code below:

```
describe('Math operations', () => {
  test('adds 1 + 2 to equal 3', () => {
    expect(1 + 2).toBe(3);
  });

  test('multiplies 3 by 4 to equal 12', () => {
    expect(3 * 4).toBe(12);
  });
});

```
2) test(): The test function is used to define individual test cases. It takes two arguments: a string describing the test (the test name) and a callback function that contains the test logic.  For example look at the code below:

```
test('adds 1 + 2 to equal 3', () => {
  expect(1 + 2).toBe(3);
});

```
Jest also includes other testing options such mocking, assertions, e.t.c. 

**Installing and setting up Jest**
Now that we have understood what jest is all about, let's go back to our application and test it out with jest. You can install jest using your favorite package manager.

**npm**

```
npm install --save-dev jest
```
**yarn**

```
yarn add --dev jest
```
**pnpm**

```
pnpm add --save-dev jest
```
Once installation is complete, go to your package.json and make a small change. Take a look at the code below

```
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
```
Replace the above with  "test": :"jest".

Now run the code below  to test your app. 

```
 npm test
```
You will see the following when you run the code.

```
No tests found, exiting with code 1
```

**Lets write our first unit test**

Create a directory named __tests__ (double underscore) in your project directory to store your test files. Inside the __tests__ directory, create a file named task-manager.test.js.

Now paste the following code. 

```
// __tests__/task-manager.test.js

const taskManager = require('../task-manager');

test('addTask should add a task to the task list', () => {
  taskManager.addTask('Buy groceries');
  expect(taskManager.listTasks()).toEqual([{ task: 'Buy groceries', completed: false }]);
});

test('completeTask should mark a task as completed', () => {
  taskManager.completeTask(0);
  expect(taskManager.listTasks()[0].completed).toBe(true);
});

test('listTasks should return the list of tasks', () => {
  expect(taskManager.listTasks()).toEqual([{ task: 'Buy groceries', completed: true }]);
});

```

Run your code with npm test. You will see the following result.

```
 PASS  __tests__/task-manager.test.js
  ✓ addTask should add a task to the task list (2 ms)
  ✓ completeTask should mark a task as completed
  ✓ listTasks should return the list of tasks (4 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        0.35 s
Ran all test suites.
```
Finally, let's make our test fail by modifying the code in the task manager file. It is crucial to see your tests fail since it decreases the risk of error and defects. Instead, I set the completed to true in the following code. 


```
// task-manager.js

function addTask(task) {
  tasks.push(
    { 
        task, 
        completed: true
    }
    );
}
```
Now this is what a failed test would look like 

```
 FAIL  __tests__/task-manager.test.js
  ✕ addTask should add a task to the task list (7 ms)
  ✓ completeTask should mark a task as completed (1 ms)
  ✓ listTasks should return the list of tasks (1 ms)

  ● addTask should add a task to the task list

    expect(received).toEqual(expected) // deep equality

    - Expected  - 1
    + Received  + 1

      Array [
        Object {
    -     "completed": false,
    +     "completed": true,
          "task": "Buy groceries",
        },
      ]

      3 | test('addTask should add a task to the task list', () => {
      4 |   taskManager.addTask('Buy groceries');
    > 5 |   expect(taskManager.listTasks()).toEqual([{ task: 'Buy groceries', completed: false }]);
        |                                   ^
      6 | });
      7 |
      8 | test('completeTask should mark a task as completed', () => {

      at Object.toEqual (__tests__/task-manager.test.js:5:35)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 2 passed, 3 total
Snapshots:   0 total
Time:        0.342 s, estimated 1 s
Ran all test suites.
```

As you can see, our test provided us with feedback. So we know where to focus our efforts in our code. 

Congratulations on the completion of your first unit test. You're on your way to becoming a fantastic developer.
To learn learn more you can check out the [jest](https://jestjs.io) documentation itself. 

**Benefits of Unit testing**

1. **High code quality**: Unit testing ensures that every component of your code works properly and meets quality standards.

2. **Early Bug Identification**: Unit testing aids in the detection of bugs early in the [SDLC](https://medium.com/p/8f3f4448a333), lowering development costs and requiring less time to fix bugs. 

3. **Documentation**: Unit tests serve as documentation by demonstrating how components are expected to function. Unit tests can help new developers understand the behavior of the code.

4. **Easier Scaling**: Having a comprehensive suite of unit tests becomes increasingly important as your software grows. It serves as the foundation for scaling your application while maintaining its dependability.

5. **Enhanced User Experience**: Unit tests help ensure that the software meets the specified requirements, which, in turn, leads to a more positive user experience with fewer functional issues.

The isolation of issues and a small portion of the application is the only disadvantage of unit testing. Users do not concentrate on minor details within the app, but rather on the app as a whole. That is why it is critical to conduct end-to-end testing. 

Overall, unit testing is an important part of software development because it helps to improve code quality, increase productivity, and lower long-term costs. It's a useful technique for creating dependable and maintainable software.

To top it all off, unit testing is only one component of a well-rounded testing strategy. It is not a substitute for other types of testing, such as integration and end-to-end testing, which are required to validate the overall functionality, user experience, and interactions throughout the application. A well-rounded testing strategy employs a variety of testing techniques to ensure that an application meets user expectations and functions properly as a whole.