ctimmerm / axios-mock-adapter

Axios adapter that allows to easily mock requests
MIT License
3.47k stars 245 forks source link

axios-mock-adapter breaks when used on axios.create() instance after first test #346

Open MrAngry777 opened 2 years ago

MrAngry777 commented 2 years ago

I am using axios-mock-adapter to test my components. The strange thing is that only a single test can be run successfully because the next one always breaks. Each test works fine if run individually.

Here is my setup.

 "axios-mock-adapter": "^1.21.1",
 "axios": "^0.27.2",
 "@testing-library/jest-dom": "^5.16.1",
 "react": "^17.0.2",
 "typescript": "^4.5.4",

util.ts

export const client = axios.create({
    baseURL: process.env.REACT_APP_API_URL ? process.env.REACT_APP_API_URL : "",
    timeout: 10000,
});

The exported client is used internally by all my components

component.test.tsx

import MockAdapter from "axios-mock-adapter";
import {apiUrls, client} from "utils";
import {DUMMY_RESPONSE} from "dummy/data";
import {render, screen, waitFor} from "@testing-library/react";
import {MemoryRouter} from "react-router-dom";
import MyComponent from "MyComponent";

describe('MyComponent', () => {
    let mockClient: MockAdapter;
    beforeAll(() => {
        mockClient = new MockAdapter(client);
    })
    afterEach(() => {
        mockClient.reset()

    })
    afterAll(()=>{
        mockClient.restore()
    })

    it('should fetch images and render them', async () => {
        mockClient.onGet(apiUrls.myURL).reply(200, DUMMY_RESPONSE)
        const spy = jest.spyOn(client, 'get')
        render(
            <MemoryRouter>
                <MyComponent handleSubmit={() => {
                }}/>
            </MemoryRouter>)
        await waitFor(() => expect(spy).toBeCalledWith(apiUrls.fetchSecurityImages))
        await waitFor(() => expect(spy).toBeCalledTimes(1))
        await waitFor(() => {
            DUMMY_RESPONSE.forEach((image) => {
                expect(screen.getByAltText(image.alt)).toBeDefined()
            })
        })
        spy.mockClear()
    });
    it('should show loader while fetching images', async () => {
        mockClient.onGet(apiUrls.myURL).reply(200, DUMMY_RESPONSE)
        const spy = jest.spyOn(client, 'get')
        render(
            <MemoryRouter>
                <MyComponent handleSubmit={() => {
                }}/>
            </MemoryRouter>)

        await waitFor(() => expect(spy).toBeCalledWith(apiUrls.myURL))
        await waitFor(() => expect(spy).toBeCalledTimes(1))
        await waitFor(() => {
            DUMMY_RESPONSE.forEach((image) => {
                expect(screen.getByAltText(image.alt)).toBeDefined()
            })
        })
        spy.mockClear()
    });
});

The second test (I mean the second that is run not the order they are defined) always breaks because of an error in the useEffect() hook which throws:

Cannot read properties of undefined (reading 'data')
TypeError: Cannot read properties of undefined (reading 'data')

And this is the function that breaks:

useEffect(() => {
        const fetchImages = async () => {
            try {
                const response = await client.get(apiUrls.fetchImages) // <--- here we get undefined
                setImages(response.data)
            } catch (e: any) {
                handleErrors(e)
            }
        }
        fetchImages()
    }, [])

BTW I am aware those tests look the same. That is exactly the point. Earlier there was a delay introduced in the second one but while debugging I changed them to pinpoint the problem but with no luck.

sinner commented 2 years ago

I think I have the same issue.

raohammad commented 1 year ago

same issue

murrayee commented 1 year ago

same

changwoolab commented 1 year ago

Same issue

changwoolab commented 1 year ago

In my case, after deleting jest.restoreAllMocks and jest.resetAllMocks in afterEach, it worked. I think jest.resetAllMocks and jest.restoreAllMocks also resets axios mock, and in the test suite No-mocked axios instance is used.