Testing custom hooks react typescript

946 Views Asked by At

Im learning react testing and i have this hook i want to test but i have no idea how

import { useState, useCallback } from 'react';
import axios from 'axios';

export const useFetch = () => {
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  const fetchData = useCallback(async (url: string) => {
    setLoading(true);
    try {
      const response = await axios.get(url);
      const data = await response.data.data;
      setLoading(false);
      return data;
    } catch (error: any) {
      if (error.name !== 'AbortError') {
        setLoading(false);
        setError(error.message);
      }
    }
  }, []);

  return { error, loading, fetchData };


};

what i got so far but this is just me trying stuff cuzz there is no much help online that is good for my case

import { useFetch } from '../../hooks/useFetch';
import mockAxios from 'jest-mock-axios';
import axios from 'axios';
import { renderHook } from '@testing-library/react-hooks';

jest.mock('axios');

describe('useFetch', () => {
  afterEach(() => {
    mockAxios.reset();
  });
  it('fetches successfully data from an API', async () => {
    const { result,waitFor  } = renderHook(() => useFetch());
    let responseObj = { data: 'test response' };
    
    mockAxios.mockResponseFor({ url: '/get' }, responseObj);
    expect(result.current.error).toBe(null);
    await expect(result.current.fetchData('react')).resolves.toBe(responseObj);
  });
});
1

There are 1 best solutions below

3
Lin Du On

When you try to call setState mutate the state. We need to use act() function to

make sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions.

Since your fetchData returns a promise, we need to use async act(...), you need React version at least v16.9.0-alpha.0. For more info about act() function, see react-act-example.

react-hook-testing-library re-export act() function from chosen renderer.

Besides, I just use jest.spyOn(axios, 'get').mockResolvedValueOnce() to mock axios.get() method without installing any extra package.

Note: I return response.data, not await response.data.data.

Now, the working unit test should be:

useFetch.ts:

import { useState, useCallback } from 'react';
import axios from 'axios';

export const useFetch = () => {
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);

  const fetchData = useCallback(async (url: string) => {
    setLoading(true);
    try {
      const response = await axios.get(url);
      const data = response.data;
      setLoading(false);
      return data;
    } catch (error: any) {
      if (error.name !== 'AbortError') {
        setLoading(false);
        setError(error.message);
      }
    }
  }, []);

  return { error, loading, fetchData };
};

useFetch.test.ts:

import { useFetch } from './useFetch';
import axios from 'axios';
import { renderHook, act } from '@testing-library/react-hooks';

describe('useFetch', () => {
  it('fetches successfully data from an API', async () => {
    const mockData = { data: 'test response' };
    const axiosGetSpy = jest.spyOn(axios, 'get').mockResolvedValueOnce({ data: mockData })
    const { result } = renderHook(() => useFetch());

    expect(result.current.error).toBe(null);

    await act(async () => {
      await expect(result.current.fetchData('react')).resolves.toEqual(mockData);
    })

    axiosGetSpy.mockRestore();
  });
});

Test result:

 PASS  stackoverflow/74063562/useFetch.test.ts (17.219 s)
  useFetch
    ✓ fetches successfully data from an API (16 ms)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |   83.33 |        0 |     100 |   81.25 |                   
 useFetch.ts |   83.33 |        0 |     100 |   81.25 | 16-18             
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        21.31 s

package versions:

"@testing-library/react-hooks": "^8.0.1",
"jest": "^26.6.3",
"react": "^16.14.0",
"react-dom": "^16.14.0",