Onjsdev

Share


Jest Mocking


By onjsdev

Apr 13th, 2024

If you want to control the behavior of certain functions or modules and isolate parts of your codebase while testing your software with Jest, then mocking technique is for you. In this article, we’ll dive into Jest mocks and explore how they can be used effectively in your testing workflow.

What is Mocking?

In software development, mocking is a testing technique that involves creating imitation versions, or mocks of objects, functions, or modules to isolate the code you’re testing. By using mocks, you can have control over how these elements behave without actually executing their real code.

Mocking lets you concentrate on testing a particular piece of code while pretending that the surrounding components are functioning as expected. This isolation helps uncover bugs and errors specific to the code under examination.

To list the benefits of mocking, they includes:

  • Isolation for Precise Testing
  • Controlled Behavior
  • Handling Unavailable or Unstable Dependencies
  • Reduced Costs
  • Easy Simulations

When To Use Mocking?

Mocking is particularly helpful when dealing with external dependencies, such as APIs or databases. By creating mock versions of these dependencies, you can simulate their behavior and ensure your code interacts correctly with them, all without the complexities of real-world connections.

For example, imagine you have a function responsible for registering new users and sending them a welcome email. Instead of sending actual emails and relying on external email services, you can create a mock email service to simulate email sending behavior. This approach allows you to test different phases of the user registration process by isolating them from the external email module.

Mocks in Jest

Jest comes with a powerful mocking feature. The different types of mocking you can perform in Jest include:

  • Function Mocks
  • Module Mocks
  • Spies

Function Mocks

In Jest, function mocks allow you to define the behavior of functions without actually executing their code. Jest provides a simple way to create function mocks using the jest.fn() method. This method creates a new mock function that you can customize according to your test requirements.

For example, you can define a mock function and set its return value using the mockReturnValue method.

    const myFunction = jest.fn();
    
    myFunction.mockReturnValue(42);
    
    // Test example
    expect(myFunction()).toBe(42);

Alternatively, you can specify an entire custom implementation for the mock function using the mockImplementation method instead of controlling a part of the original function.

    const myFunction = jest.fn();
    
    myFunction.mockImplementation((a, b) => a + b);
    const result = myFunction(1,2)
    
    // Test example
    expect(result).toBe(3);
    expect(myFunction).toHaveBeenCalledWith(1, 2);

Additionally, for asynchronous functions, you can also use methods like mockResolvedValue, mockRejectedValue, etc., to define their resolved or rejected values.

    const myFunction = jest.fn();
    
    myFunction.mockResolvedValue("Promise resolved successfully!");
    // or
    myFunction.mockRejectedValue(new Error("Error Message"));
    
    expect(myFunction()).rejects.toThrow(new Error("Error Message"));

Module Mocks

In Jest, module mocks enable you to o simulate the behavior of external or internal modules. These modules can include databases, external services, APIs, or even other parts of the same application.

Suppose you have an internal math.js module that performs math operations

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

You can mock an entire module and and control the behavior of that module’s functions. For instance, consider the following example where we replace a function’s summation functionality with subtraction.

    // math.test.js
    
    const math = require("./math.js");
    
    // Mocking the dependency
    jest.mock("./math.js");
    
    describe("add", () => {
      it("should add two numbers", () => {
        // Mock the add function to change behaviour of the function
        // Now, the add fucntion will do subtraction
        math.add.mockImplementation((a, b) => a - b);
    
        // Call the function to be tested
        const result = math.add(6, 4);
    
        // Assert the result
        expect(result).toBe(2);
      });
    
      // You can add more test cases for the add function here
    });
    

You can also mock an external module. Let’s say you have a module named api.js that makes HTTP requests with Axios, and you want to mock Axios behavior for testing purposes.

    // api.js
    const axios = require('axios');
    
    async function fetchData() {
      const response = await axios.get('https://api.example.com/data');
      return response.data;
    }
    
    module.exports = { fetchData };

At this point, you can control Axios’s behavior by setting a new value for the response that the Axios get method will resolve. This means that you isolate the code you’re testing from Axios, enabling you to test other processes, such as checking if the response displays correctly in the page or if the user sees an error when it occurs.

    // api.test.js
    const axios = require('axios');
    const api = require('./api');
    
    jest.mock('axios'); // This line mocks the axios module
    
    test('fetchData should return mock data', async () => {
      axios.get.mockResolvedValue({ data: 'mocked data' });
    
      const result = await api.fetchData();
    
      // This is for
      expect(result).toBe('mocked data');
      
    });

Spies

In Jest, Spies allow you watch over function calls as an additional feature. They captures information about their invocations. This makes it a dynamic tool for both monitoring and manipulating function behavior during testing.

Jest provides a built-in spying mechanism through the jest.spyOn function. It allows you to create a spy for a specific function. Consider the following example where jest.spyOn is used to create a spy for the add function in the mathUtils module. Then, various assertions are made using the spy, such as checking if the function was called with specific arguments and how many times it was called.

    // Example module
    const mathUtils = {
      add: (a, b) => a + b,
      subtract: (a, b) => a - b,
    };
    
    // Test case
    test('Testing the add function', () => {
      // Create a spy on the 'add' function
      const addSpy = jest.spyOn(mathUtils, 'add');
      
      addSpy.mockImplementation((a,b) => a * b);
    
      // Call the function
      const result = mathUtils.add(2, 3);
    
      // Assertions
      expect(result).toBe(6); // Check the result
      expect(addSpy).toHaveBeenCalledTimes(1); // Check if the spy was called once
      expect(addSpy).toHaveBeenCalledWith(2, 3); // Check if the spy was called with specific arguments
    
      // Restore the original function (optional)
      addSpy.mockRestore();
    });
    

Actually, as you may realize jest.spyOn and jest.fn are so similar, but they serve slightly different purposes. jest.spyOn is used to create a spy on existing functions or methods, especially when you want to temporarily mock or change their behavior during testing. On the other hand, jest.fn is used to create standalone mock functions that you can use to define custom behavior or replace functions entirely during testing.

Conclusion

In summary, using mocks in testing allows you to isolate the code you are testing from certain dependencies, functions, or modules. They help you create a controlled environment, catch bugs early, and ensure that your code interacts seamlessly with its dependencies, all while reducing costs and improving overall efficiency.

Remember that, using mocks does not mean that you should avoid testing external modules. They allow you to test other processes except for those modules.

In this article we have discussed the different types of mocking in Jest along with examples.

Thank you for reading.