Reputation: 7717
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.
Upvotes: 12
Views: 36613
Reputation: 9913
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,
},
],
};
});
Upvotes: 0
Reputation: 1452
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();
});
Upvotes: 13
Reputation: 102207
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
Upvotes: 1