Share


Jest Tutorial: Testing In JavaScript


By gobrain

Apr 17th, 2024

Testing is a crucial aspect of software development that ensures the reliability and correctness of the codebase. Writing tests helps in identifying bugs and issues in the development process.

When it comes to testing in JavaScript, Jest is a popular testing framework widely used in the development community due to its ease of use, speed, and powerful features

In this article, we will explore the basics of testing with Jest and how to set up and execute tests effectively.

What is Jest?

Jest is an open-source testing framework developed by Facebook. It is designed specifically for testing JavaScript applications, particularly React applications. The framework comes bundled with various utilities that enable snapshot testing, mocking, and code coverage analysis, making it a comprehensive solution for testing JavaScript code.

Getting Started with Jest

To begin using Jest for testing your JavaScript projects, you need to set up Jest as a dev dependency in your project. You can install it using npm or yarn, depending on your preferred package manager:

npm install jest --save-dev

Once Jest is installed, you can configure it to run tests. The default configuration for Jest is simple and should work out of the box for most projects. However, for more complex projects, you can customize the configuration by creating a jest.config.js file in the root of your project.

Writing Tests with Jest

In Jest, the describe function is a fundamental building block used for organizing and grouping related test cases. In it, Jest allows you to write test cases using the test() or it() functions, which take a description of the test and a function containing the actual test code.

The test function typically contains the logic to be tested and some assertions to verify the expected behavior.

Let’s look at a simple example to illustrate how to write a test using Jest:

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

    const sum = require("./sum");
    
    describe("Test sum function", () => {
      test("adds 1 + 2 to equal 3", () => {
        expect(sum(1, 2)).toBe(3);
      });
    });

In this example, we have a simple sum function that adds two numbers. We then create a test file named sum.test.js, and within this file, we import the sum function and write a test case using the test() function. The expect() function is used to define the expected outcome, and toBe() is the matcher to check if the result matches the expected value.

Running Jest Tests

After writing the test cases, we need to execute them using Jest. Jest provides a convenient command-line interface to run tests. By default, Jest looks for test files in the __tests__ folder or any file with a .test.js or .spec.js extension.

To execute the tests, you can run the following command:

npx jest

Jest will automatically find and run all the test files in the project and display the test results in the console.

Testing with jest

Assertions

In Jest, assertions are statements that check whether the actual result of an operation matches the expected result. Jest provides various assertion functions to compare and validate values. Some commonly used ones include:

  • expect(value) – The starting point for any assertion.
  • .toBe(expected) – Tests exact equality (for primitive values like numbers, strings, etc.).
  • .toEqual(expected) – Tests deep equality (for objects and arrays).
  • .toBeTruthy() – Tests if a value is truthy.
  • .toBeFalsy() – Tests if a value is falsy.
  • .not.toBe(expected) – Tests for inequality.

For example, toContain assertion can be used to check whether an array contain in a specific element as shown below:

test('checks if an array contains a specific element', () => {
  const myArray = [1, 2, 3];
  expect(myArray).toContain(2);
});

Or, the toThrow assertions is used to check if a function throws an exception when called.

function divide(a, b) {
  if (b === 0) throw new Error('Division by zero');
  return a / b;
}

test('throws an error when dividing by zero', () => {
  expect(() => divide(10, 0)).toThrow('Division by zero');
});

These a few assertion examples in Jest, you can visit the Jest documentation for more assertion function.

Mocking

Mocking is a technique used in testing to create and control specific behavior of objects or functions that a unit being tested interacts with. In Jest, mocking is a powerful feature that allows you to replace real dependencies with “mock” implementations.

Jest API for mocking includes:

  • jest.fn(): Creates a mock function.
  • jest.mock('module-name'): Automatically mocks a module or dependency.
  • jest.spyOn(object, methodName): Creates a spy on an existing method of an object, allowing you to track calls and return values.

Now, suppose you have a simple function that fetches data from an API and processes it:

const axios = require("axios");

async function fetchData() {
  try {
    const response = await axios.get(
      "https://jsonplaceholder.typicode.com/posts/1"
    );
    return response.data;
  } catch (error) {
    throw new Error("Failed to fetch data");
  }
}

module.exports = { fetchData };

In this example, we’re using the JSONPlaceholder Fake API (https://jsonplaceholder.typicode.com) to fetch a sample post. The fetchData function makes an HTTP GET request using axios and returns the data.

You can write a test for this function with mocking as follows:

// .fetchData.test.js
const { fetchData } = require("./fetchData");
const axios = require("axios");

jest.mock("axios"); // Automatically mocks axios

test("fetchData function should return data", async () => {
  const mockData = { title: "Mocked Title", body: "Mocked Body" };
  axios.get.mockResolvedValue({ data: mockData });

  const data = await fetchData();

  expect(data).toEqual(mockData);
});

test("fetchData function should throw an error on API error", async () => {
  const errorMessage = "Network Error";
  axios.get.mockRejectedValue(new Error(errorMessage));

  await expect(fetchData()).rejects.toThrow("Failed to fetch data");
});

In the test file, we use jest.mock to automatically mock axios, and then we use axios.get.mockResolvedValue to simulate a successful API response with the mockData. We also test the scenario where the API call fails by using axios.get.mockRejectedValue.

Asynchronous Testing

Asynchronous testing in Jest allows you to write and run tests that involve asynchronous operations, such as network requests, timers, or Promises. For testing asynchronous code (e.g., async/await, Promises, callbacks), use Jest’s async and await, or the done callback for callbacks.

  • Using done callback: The simplest way to handle asynchronous testing is by using the done callback. In this approach, you call done when your asynchronous code is complete, and Jest will wait until the done callback is called or a timeout occurs.

    test('example asynchronous test', (done) => { // Assume some asynchronous code or operation setTimeout(() => { // Your test assertions go here expect(true).toBe(true);

      // Call the done callback to signal the test is complete
      done();
    }, 1000); // This test will wait for at most 1000ms for the done callback
    

    });

  • Using async/await

Jest also supports using async/await syntax, which makes your asynchronous tests look more like synchronous code. When you return a Promise from the test function, Jest will automatically wait for the Promise to resolve.

test('example asynchronous test with async/await', async () => {
  // Assume some asynchronous code or operation
  await someAsyncFunction();

  // Your test assertions go here
  expect(true).toBe(true);
});
  • Using resolves and rejects matchers

If you’re testing functions that return Promises, Jest provides special matchers to handle Promise resolution and rejection.

// For resolving Promises
test('example asynchronous test with resolves', () => {
  return expect(someAsyncFunction()).resolves.toBe('expectedValue');
});

// For rejecting Promises
test('example asynchronous test with rejects', () => {
  return expect(someAsyncFunction()).rejects.toThrow('error message');
});

Conclusion

Testing with Jest is an essential part of modern JavaScript development. It provides a simple yet powerful framework to write and execute tests.

Remember that writing effective tests requires practice and attention to detail. As your project grows, invest time in maintaining and expanding your test suite to cover new features and potential edge cases.

Thank you for reading.