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.
In order to mock asynchronous code in Jest, more specifically Promises, you can use the mockResolvedValue function. This will mock the return value of the Promise to be 42. In order to test a Promise in Jest, you need to turn your it block into async in order to use the await keyword in front of an expect statement.
In Jest, Node. js modules are automatically mocked in your tests when you place the mock files in a __mocks__ folder that's next to the node_modules folder. For example, if you a file called __mock__/fs. js , then every time the fs module is called in your test, Jest will automatically use the mocks.
The spyOn function is one of the most powerful utility functions in Jest. It allows you to spy on a function, observe interactions, and mock them accordingly. But to spy on a named import in Jest isn't straightforward due to the arguments that the function accepts.
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();
});
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With