使用 Testing Library/React-Hooks 测试自定义挂钩

Testing custom hooks with Testing Library/React-Hooks

我有更多关于 testing-library/react-hooks 的概念性问题。

我有以下测试:

  describe('something else', () => {
    const mock = jest.fn().mockResolvedValue({ data: [['NL', 'Netherlands'], ['CU', 'Cuba']], status: 200 });
    axios.get = mock;

    it('calls the api once', async () => {
      const setItemMock = jest.fn();
      const getItemMock = jest.fn();
      global.sessionStorage = jest.fn();
      global.sessionStorage.setItem = setItemMock;
      global.sessionStorage.getItem = getItemMock;

      const { waitFor } = renderHook(() => useCountries());

      await waitFor(() => expect(setItemMock).toHaveBeenCalledTimes(0));
    });
  });

测试以下自定义挂钩:

import { useEffect, useState } from 'react';
import axios from '../../shared/utils/axiosDefault';
import { locale } from '../../../config/locales/locale';

type TUseCountriesReturnProps = {
  countries: [string, string][];
  loading: boolean;
  error: string;
}

export default function useCountries(): TUseCountriesReturnProps {
  const [countries, setCountries] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const sessionStorageCountriesKey = `countries-${locale}`;

  useEffect(() => {
    const countriesFromStorage = sessionStorage.getItem(sessionStorageCountriesKey);

    const getCountries = async () => {
      try {
        const response = await axios.get('/api/v3/countries', {
          params: {
            locale,
          },
        });
        console.log(response);

        if (response.status === 200) {
          setCountries(response.data);
          sessionStorage.setItem(sessionStorageCountriesKey, JSON.stringify(response.data));
        } else {
          console.error(response);
          setError(`Error loading countries, ${response.status}`);
        }
      } catch (error) {
        console.error(error);
        setError('Failed to load countries');
      }
    };

    if (!countriesFromStorage) {
      getCountries();
    } else {
      setCountries(JSON.parse(countriesFromStorage));
    }

    setLoading(false);
  }, []);

  return {
    countries,
    loading,
    error,
  };
}

如果我将 toHaveBeenCalledTimes(1) 更改为 toHaveBeenCalledTimes(0),我会突然在

上得到一个 Warning: An update to TestComponent inside a test was not wrapped in act(...).
      29 |         if (response.status === 200) {
    > 30 |           setCountries(response.data);

如果我做任何大于 1 的数字,它就会超时。即使我将超时时间延长到 30 秒,它也只是超时。这里发生了什么。我只是不明白。所有这些让我想知道它是否真的 运行 测试正确。

好的,我想我明白了。由于某种原因,等待在这种情况下不起作用。我现在按如下方式进行,并且适用于所有情况:

describe('useCountries', () => {
  describe('when initialising without anything in the sessionStorage', () => {
    beforeEach(() => {
      axios.get.mockResolvedValue({ data: [['NL', 'Netherlands'], ['CU', 'Cuba']], status: 200 });
      global.sessionStorage = jest.fn();
      global.sessionStorage.getItem = jest.fn();
    });

    afterEach(() => {
      jest.clearAllMocks();
    });

    it('calls session storage set item once', async () => {
      const setItemMock = jest.fn();
      global.sessionStorage.setItem = setItemMock;

      const { waitForNextUpdate } = renderHook(() => useCountries());
      await waitForNextUpdate();

      expect(setItemMock).toHaveBeenCalledTimes(1);
    });
  });
});

所以测试库似乎希望您只等待发生的第一个更新,而不是直到它停止执行操作。一旦等待最终结果,其他更新就会触发,而这些更新会以某种方式在内部搞砸。我希望我能更明确地说明原因,但至少 waitForNextUpdate 似乎已经解决了我的问题。