How To Mock Only One Function From Module
Tags: testing, javascript, typescript, tutorial
Published: 2023-02-24 14:28:33

Mocking functions in Jest can be a powerful tool for testing your code, but sometimes you just want to mock one function from a module while leaving the others intact. Here's how to do it.

Let's say you're working on a project where you're making REST API requests to a third-party service to retrieve user data. You have a module called `userService` that contains functions for these requests, such as `getUserById` or `listUsers`. Internally, these functions use the `request` function from the `rest-api-request` module to execute the REST API query.

However, the API is rate-limited, and you want to avoid exceeding the rate limit during your tests. Therefore, you decide to mock the `request` function to return a fake user object, but leave the rest of the implementation unchanged to avoid side effects.

```ts
import userService from './userService';
import { request } from 'rest-api-request';

jest.mock('rest-api-request', () => {
  const original = jest.requireActual('rest-api-request');

  return {
    ...original,
    request: jest.fn(() => ({
      id: 1,
      name: 'John Doe',
      email: 'john.doe@example.com',
    })),
  };
});
```

In this example, we use the [`jest.mock()`](https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options) function to create a mock for `rest-api-request`. Then we use the [`jest.requireActual()`](https://jestjs.io/docs/jest-object#jestrequireactualmodulename) function to get the original implementation of `rest-api-request`. The original module is spread to a new object and the `request` function is overwritten with a mock implementation using [`jest.fn()`](https://jestjs.io/docs/jest-object#jestfnimplementation). In this case, the mock implementation returns only a dummy user object.

Next, this is how we would test our `userService`:

```ts
describe('userService', () => {
  test('Should call the REST API and return a user object', async () => {
    const id = 1;
    const params = {
      endpoint: `/users/${id}`,
      method: 'GET',
    };
    const expectedUser = {
      id,
      name: 'John Doe',
      email: 'john.doe@example.com',
    };

    const user = await userService.getUserById(id);
    
    // test user object was returned from mock function
    expect(user).toEqual(expectedUser);

    // test mock function was called once with correct parameters
    expect(request).toHaveBeenCalledTimes(1);
    expect(request).toHaveBeenCalledWith(params)
  });
});
```

In this test, we set up the objects that the mock function should receive and return. Then we call `getUserById` with an ID, which would normally trigger a call to `request` to execute the REST API query, but will stop at our mock function. Finally, we use Jest's [`toHaveBeenCalledTimes()`](https://jestjs.io/docs/expect#tohavebeencalledtimesnumber) and [`toHaveBeenCalledWith()`](https://jestjs.io/docs/expect#tohavebeencalledwitharg1-arg2-) matchers to check that `request` was called once and with the correct parameters.

Overall, this approach allows us to test the functionality of our user service module while ensuring that it uses the `request` function correctly. Using this technique, we can selectively mock functions in dependent modules without affecting the functionality of the module itself.