Why am I unable to use mockResolvedValue here?
Asked Answered
O

3

9

I’m unclear on how to mock my api response when using the following setup.

I have the following test:

import React from 'react';
import { cleanup, render, wait } from '@testing-library/react';
import axios from 'axios';
import Condition from './index.jsx';

jest.mock('axios', () => {
  return {
    create: jest.fn(() => ({
      get: jest.fn().mockResolvedValue({ data: {} }),
      interceptors: {
        request: { use: jest.fn(), eject: jest.fn() },
        response: { use: jest.fn(), eject: jest.fn() },
      },
    })),
  };
});


afterEach(cleanup);

test('fetches and displays data', async () => {
  axios.get.mockResolvedValue({ data: 'mock data' });
  const { container } = render(<Condition {...props} />);
  await wait(() => expect(container.textContent).toContain('Current milestone'));
  expect(container).toBeDefined();
});

… and the following api helper:

import axios from 'axios';

const api = axios.create({
  baseURL: window.apiPath,
  withCredentials: true,
});

api.interceptors.request.use(config => {
  const newConfig = Object.assign({}, config);
  newConfig.headers.Accept = 'application/json';

  return newConfig;
}, error => Promise.reject(error));

export default api;

My Condition component uses the following function to fetch data when it mounts:

const fetchAsync = async endpoint => {
  const result = await api.get(endpoint);
  setSuffixOptions(result.data.data);
  console.log('result.data.data', result.data.data);
};

The axios.get.mockResolvedValue({ data: 'mock data' }); line in the test causes the following error:

    TypeError: Cannot read property 'mockResolvedValue' of undefined

      124 |
      125 | test('fetches and displays data', async () => {
    > 126 |   axios.get.mockResolvedValue({ data: 'mock data' });
          |             ^
      127 |   const { container } = render(<Condition {...props} />);
      128 |   await wait(() => expect(container.textContent).toContain('Current milestone'));
      129 |   expect(container).toBeDefined();

Should I be using a different method for mocking the response?

EDIT Calling axios.create.get.mockResolvedValue({ data: 'mock data' }); results in the same error.

Otway answered 20/9, 2020 at 17:18 Comment(5)
Because your Axios test double only has a create prop, no get.Prasad
But the create method is what creates the api which has the get and interceptors methods in the api file. How do I modify axios.get.mockResolvedValue for that shape?Otway
Sure, but you're not calling that in the test.Prasad
Calling axios.create.get.mockResolvedValue({ data: 'mock data' }); results in the same error.Otway
axios.create.get is trying to access the get prop on the function, it doesn't call the function.Prasad
F
11

To mock your api response, you can use jest.spyOn in combination with mockImplementation, e.g. as follows:

import api from './api';

const mock = jest.spyOn(api,"get");
mock.mockImplementation(() => Promise.resolve({ data: {} }));

In the example above, the api.get method is replaced to emulate a successful call returning { data: 'mock data' }.

It can be placed in your test function as follows:

test('fetches and displays data', async () => {
    const mock = jest.spyOn(api, "get");
    mock.mockImplementation(() => Promise.resolve({ data: {} }));
    const { container } = render(<Condition {...props} />);
    await wait(() => expect(container.textContent).toContain('Current milestone'));
    expect(container).toBeDefined();
});
Freely answered 8/10, 2020 at 17:25 Comment(3)
Where would I pass the response data in?Otway
inside Promise.resolve, like this: mock.mockImplementation(() => Promise.resolve({ data: {} }))Freely
Mind dropping that in the answer? Then I’ll accept.Otway
C
1

The better way is to mock the ./api helper module rather than axios module. Because you are testing the component which imports ./api as its direct dependency. For the component, the axios module is indirect dependency. If you want to test ./api helper, then you should mock axios module, because now, it becomes a direct dependency for ./api.

E.g.

index.tsx:

import React, { useEffect, useState } from 'react';
import api from './api';

function Condition(props) {
  const [options, setSuffixOptions] = useState();
  const fetchAsync = async (endpoint) => {
    const result = await api.get(endpoint);
    setSuffixOptions(result.data);
    console.log('result.data', result.data);
  };
  useEffect(() => {
    fetchAsync('http://localhost:3000/api');
  });

  return <div>{options}</div>;
}

export default Condition;

index.test.tsx:

import React from 'react';
import Condition from './';
import { render, wait } from '@testing-library/react';
import api from './api';
import { mocked } from 'ts-jest/utils';

jest.mock('./api', () => {
  return { get: jest.fn() };
});

describe('63981693', () => {
  test('fetches and displays data', async () => {
    mocked(api.get).mockResolvedValue({ data: 'mock data' });
    const props = {};
    const { container } = render(<Condition {...props} />);
    await wait(() => expect(container.textContent).toContain('mock data'));
    expect(api.get).toBeCalledWith('http://localhost:3000/api');
    expect(container).toBeDefined();
  });
});

We don't care about the implementation of the ./api module because we have mocked it.

unit test result with coverage report:

 PASS  src/stackoverflow/63981693/index.test.tsx (12.377s)
  63981693
    ✓ fetches and displays data (68ms)

  console.log src/stackoverflow/63981693/index.tsx:1461
    result.data mock data

  console.log src/stackoverflow/63981693/index.tsx:1461
    result.data mock data

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |      100 |      100 |      100 |      100 |                   |
 index.tsx |      100 |      100 |      100 |      100 |                   |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        14.873s, estimated 18s
Cullin answered 9/10, 2020 at 10:17 Comment(1)
You use ts-jest here but the project is not written in typescript.Otway
E
0

This is how we can mock the same method with multiple conditions:

Import the Axios:

import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;

Use the mockedAxios:

const mock = jest.spyOn(mockedAxios, 'post');
mock.mockImplementation((url): any => {
  if (url.indexOf('checkAddOnEligibility') > 0) {
    return { data: [{ validationMessage: { value: '' }, isValid: { value: true } }] };
  }
  return {
    data: [
      {
        thirdYearPremium: 48945,
        secondYearPremium: 30535,
        firstYearPremium: 2000,
      },
    ],
  };
});
Ejectment answered 26/7, 2023 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.