Unit Testing in JavaScript: A Practical Guide with Jest

2024-08-19

Unit testing is a critical part of software development, ensuring that individual units of code work as intended. In the JavaScript ecosystem, Jest is one of the most popular testing frameworks, offering a powerful and flexible way to write tests. In this guide, we'll dive into the basics of unit testing in JavaScript using Jest, along with practical examples to help you get started.

Why Unit Testing?

Before we dive into Jest, let's quickly recap why unit testing is important:

Setting Up Jest

To get started with Jest, you first need to install it in your project. If you're using npm, run the following command:

npm install --save-dev jest

Or if you're using Yarn:

yarn add --dev jest

Once installed, you can add a test script to your package.json file:

{
  "scripts": {
    "test": "jest"
  }
}

Now, you can run your tests with the command:

npm test

Writing Your First Test

Let's start with a simple example. Suppose we have a function that adds two numbers:

// sum.js
function sum(a, b) {
  return a + b;
}
 
module.exports = sum;

To test this function, create a new file named sum.test.js:

// sum.test.js
const sum = require('./sum');
 
test('adds 1 + 2 to equal 3', () => {
  expect(sum(1, 2)).toBe(3);
});

In this test, we're using Jest's test function to define a test case. The expect function is used to assert that the result of sum(1, 2) is 3.

Run the test by executing:

npm test

You should see an output indicating that the test passed.

Advanced Assertions

Jest provides a wide range of matchers for different types of assertions. Here are a few examples:

Testing Arrays and Objects

// arrayTest.js
test('object assignment', () => {
  const data = { one: 1 };
  data['two'] = 2;
  expect(data).toEqual({ one: 1, two: 2 });
});

Testing for Exceptions

// exceptionTest.js
function compileAndroidCode() {
  throw new Error('you are using the wrong JDK');
}
 
test('compiling android goes as expected', () => {
  expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
});

Mocking Functions

In real-world applications, you'll often need to test functions that depend on external modules or functions. Jest's mocking capabilities make this easy.

Basic Mock Example

Suppose we have a function that calls another function to get data:

// fetchData.js
const fetchData = () => {
  return 'some data';
};
 
module.exports = fetchData;

And another function that uses fetchData:

// processData.js
const fetchData = require('./fetchData');
 
function processData() {
  const data = fetchData();
  return `Processed: ${data}`;
}
 
module.exports = processData;

We can mock fetchData in our test:

// processData.test.js
const fetchData = require('./fetchData');
const processData = require('./processData');
 
jest.mock('./fetchData'); // Mock the fetchData module
 
test('processes data correctly', () => {
  fetchData.mockReturnValue('mocked data'); // Define the mock implementation
  expect(processData()).toBe('Processed: mocked data');
});

Testing Asynchronous Code

Jest also makes it easy to test asynchronous code. Let's say we have a function that fetches data from an API:

// asyncData.js
const fetch = require('node-fetch');
 
async function fetchData() {
  const response = await fetch('https://jsonplaceholder.typicode.com/todos/1');
  const data = await response.json();
  return data;
}
 
module.exports = fetchData;

We can write a test for this using async/await:

// asyncData.test.js
const fetchData = require('./asyncData');
 
test('fetches data from API', async () => {
  const data = await fetchData();
  expect(data).toHaveProperty('id', 1);
});

Alternatively, you can use the .resolves matcher:

test('fetches data from API', () => {
  return expect(fetchData()).resolves.toHaveProperty('id', 1);
});

Running Tests in Watch Mode

During development, it's often helpful to run tests automatically whenever you make changes. Jest provides a watch mode for this:

npm test -- --watch

This will rerun your tests every time a file changes, making it easier to catch issues as you code.

Conclusion

Unit testing is an essential part of developing reliable and maintainable software. With Jest, you have a powerful tool at your disposal