ctimmerm / axios-mock-adapter

Axios adapter that allows to easily mock requests
MIT License
3.44k stars 241 forks source link

Failed to mock requests from suspended component #309

Closed s1n7ax closed 3 weeks ago

s1n7ax commented 3 years ago

I have a component that waits until some data from a resource. I'm using React suspense to show the loading screen as until it gets the response. When testing, even though onGet is registered, in axiosMock, it never gets the request from <Cmp /> component. Test fails due to network error.

Example code:

import { render, waitFor } from '@testing-library/react';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import React, { Suspense } from 'react';

const axiosMock = new MockAdapter(axios, { onNoMatch: 'throwException' });

const wrapPromise = (promise) => {
  let status = 'pending';
  let result: any;

  promise.then(
    (r) => {
      status = 'success';
      result = r;
    },
    (e) => {
      status = 'error';
      result = e;
    }
  );

  return {
    read() {
      return {
        status,
        result,
      };
    },
  };
};

const fetchData = () => {
  return wrapPromise(axios.get('/test').then((r) => r.data));
};

const resource = fetchData();

export const Cmp = () => {
  const str = resource.read();
  return <h1>{str}</h1>;
};

describe('Main Tests', () => {
  beforeAll(() => {
    axiosMock.reset();
  });

  /*
  THIS WORKS
   */
  it('Sample Request Test', async () => {
    axiosMock.onGet('/test').reply(200, 'hello');
    expect(await axios.get('/test').then((r) => r.data)).toBe('hello');
  });

  /*
  THIS DOES NOT WORK
  */
  it('Component Request Test', async () => {
    axiosMock.onGet('/test').reply(200, 'hello');

    const { getByText } = render(
      <Suspense fallback={<p>Loading...</p>}>
        <Cmp />
      </Suspense>
    );

    await waitFor(() => {
      return getByText('hello');
    });
  });
});
marcbachmann commented 3 weeks ago

You already execute the axios request in your fetchData() request, which is too early.

const fetchData = () => {
  return wrapPromise(axios.get('/test').then((r) => r.data));
};

const resource = fetchData();

Not sure how you should structure the code with suspense, but you should delay the fetching until the component is initialized (maybe in a mounted hook or something similar)