编辑:我找到了一个解决方案,来自Kent C. Dodds的this video。
只需将jest.advanceTimersByTime调用包装在act函数中即可。
所以这个:
jest.advanceTimersByTime(510);
成为:
act(()=>jest.advanceTimersByTime(510));
我已经实现了一个简单的自定义钩子,以便对值更新进行反跳操作。
代码如下:
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
这个钩子的客户端是这样的(非常简化):
function Search(props) {
const [searchTerm, setSearchTerm] = useState('');
const debouncedSearchTerm = useDebounce(searchTerm, 500);
// doing something useful with debouncedSearchTerm....
// ...
// ...
}
因此,我正在尝试使用以下代码测试该钩子:
import { renderHook, act } from 'react-hooks-testing-library';
import useDebounce from '../useDebounce';
jest.useFakeTimers();
it.only('should update value after specified delay', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: '', delay: 500 } }
);
expect(result.current).toBe('');
jest.advanceTimersByTime(510);
expect(result.current).toBe('');
rerender({ value: 'Hello World', delay: 500 });
expect(result.current).toBe('');
jest.advanceTimersByTime(498);
expect(result.current).toBe('');
jest.advanceTimersByTime(3);
expect(result.current).toBe('Hello World');
});
尽管测试通过了,但我收到以下警告:
console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:102
Warning: An update to TestHook inside a test was not wrapped in act(...). When testing, code that causes React state updates should be wrapped into act(...): act(() => { /* fire events that update state */ }); /* assert on the output */ This ensures that you're testing the behavior the user would see in the browser. Learn more at..... in TestHook in Suspense
我知道,如果我要调用一个函数以更新挂钩的内部状态(例如useCounter挂钩的增量()),则必须在 act 功能,如文档所述。
但是,我实现的useDebounce挂钩通过内部useEffect更改状态,该useEffect在值或延迟更改时运行。
如何摆脱这种警告?我的代码有问题吗?我忘了在测试代码中添加一些内容吗?
请帮助!
答案 0 :(得分:0)
将此行包裹到 act
中:
it.only('should update value after specified delay', () => {
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{ initialProps: { value: '', delay: 500 } }
);
expect(result.current).toBe('');
jest.advanceTimersByTime(510);
expect(result.current).toBe('');
rerender({ value: 'Hello World', delay: 500 });
expect(result.current).toBe('');
jest.advanceTimersByTime(498);
expect(result.current).toBe('');
// once delay time is passed, component is rerendered, so to capture that
// in unit test, this line should be wrapped in act
act(() => jest.advanceTimersByTime(3));
expect(result.current).toBe('Hello World');
});```