使用 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
似乎已经解决了我的问题。
我有更多关于 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
似乎已经解决了我的问题。