Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing delayed custom React hook with Jest/Enzyme?

I'm trying to test a custom hook that uses useState and useEffect together with a setTimeout that simulates a delay loading some data. Simplified

const useCustomHook = (id: number) => {
  const [loading, setLoading] = React.useState(true);
  const [error, setError] = React.useState(false);
  const [value, setValue] = React.useState<string>();

  React.useEffect(() => {
    const dummy = ["foo", "bar", "baz"];
    // simulate remote call with delay
    setTimeout(() => {
      id < 3 ? setValue(dummy[id]) : setError(true);
      setLoading(false);
    }, 1500);
  }, [id]);

  return [loading, error, value];
};

const App = () => {
  const [loading, error, value] = useCustomHook(1);

  if (loading) { return <div>Loading...</div>; }
  if (error) { return <div>Error</div>; }
  return <h1>{value}</h1>;
};

https://codesandbox.io/s/react-typescript-z1z2b

How would you test all possible states (loading, error and value) of this hook with Jest and Enzyme?

Thanks in advance!!!

like image 367
Jan Rembold Avatar asked May 19 '26 08:05

Jan Rembold


1 Answers

I guess what you really want is to send an API request inside the useEffect hooks. (sorry if I misunderstand your purpose)

If so, I will check

  • API request has been sent
  • Show loading at first
  • Show error when API request failed
  • Show result when API request success

The test should look like this.

describe('App', () => {
  beforeEach(() => {
    fetch.resetMocks();
  });

  it('should fetch the request', async () => {
    await act(async () => {
      await mount(<App />)
    })

    expect(fetch).toBeCalledWith('https://dog.ceo/api/breeds/image/random');
  });

  it('should show loading at first', () => {
    // mock useEffect to test the status before the API request
    jest
      .spyOn(React, 'useEffect')
      .mockImplementationOnce(() => {}); 

    const comp = mount(<App />)

    expect(comp.text()).toBe('Loading...');
  });

  it('should display error if api request fail', async () => {
    fetch.mockRejectOnce();
    let comp;

    await act(async () => {
      comp = await mount(<App />);
    })
    comp.update();

    expect(comp.text()).toBe('Error');
  });

  it('should display result if api request success', async () => {
    fetch.mockResponseOnce(JSON.stringify({
      message: "https://images.dog.ceo/breeds/mastiff-tibetan/n02108551_1287.jpg",
      status: "success"
    }));
    let comp;

    await act(async () => {
      comp = await mount(<App />);
    })
    comp.update();


    expect(comp.find('img')).toHaveLength(1);
    expect(comp.find('img').prop('src'))
      .toBe('https://images.dog.ceo/breeds/mastiff-tibetan/n02108551_1287.jpg');
  });
});

here is the repo for reference: https://github.com/oahehc/stackoverflow-answers/tree/60514934/src

Furthermore, you pass an id into useCustomHook, I guess this will be used as a parameter in API request. So you might want to add more test cases to check that part.

like image 161
Andrew - oahehc Avatar answered May 22 '26 01:05

Andrew - oahehc



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!