如何用两个 useEffect 语句测试钩子?
How to test hooks with two useEffect statements?
我有以下钩子:
const useBar = () => {
const [myFoo, setFoo] = useState(0);
const [myBar, setBar] = useState(0);
useEffect(() => {
setFoo(myFoo + 1);
console.log("setting foo (1)", myFoo, myBar);
}, [setFoo, myFoo, myBar]);
useEffect(() => {
setBar(myBar + 1);
console.log("setting bar (2)", myFoo, myBar);
}, [setBar, myBar, myFoo]);
};
使用组件时,我有预期的无限循环行为:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Bar = () => {
useBar();
return <div>Bar</div>;
};
function App() {
return (
<Bar />
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
来自 console.log :
setting foo (1) 1 1
setting bar (2) 1 1
setting foo (1) 2 2
setting bar (2) 2 2
setting foo (1) 3 3
setting bar (2) 3 3
...
然而,使用 @testing-library/react-hooks
进行测试仅给出第一个循环输出:
describe("Tests", () => {
test("useBar", async () => {
const { rerender } = renderHook(() => {
return useBar();
});
// await waitForNextUpdate();
// tried the above doesn't work
// rerender();
// with rerender it loops through the next "cycle"
});
});
想知道如何正确测试挂钩,而不调用 rerender
并模仿与 React 相同的行为 - 在状态更改时重新渲染组件。
我猜你想测试一个使用useEffect
的hook
,当出现死循环时报错。
但实际上我们无法测试死循环,只能对次数进行限制。当循环超过这个限制时,我们认为它会无限循环下去。至于是否真的是无限循环,不好证明。
然而,要测试 useEffect
的重复并不容易。
虽然使用waitForNextUpdate
会出现超时错误,但是这个错误也可能是长时间的异步操作导致的,作为单元测试需要快速反馈。你不能每次都等待超时。这是浪费时间。
感谢这个问题,我找到了方向Provide a way to trigger useEffect from tests
当我用 useLayoutEffect
替换 useEffect
时,我可以快速准确地得到 Maximum update depth exceeded
错误。这只需要将要求的测试中的useEffect
替换为useLayoutEffect
即可。当循环无限时,测试将失败。
import React from 'react'
import { renderHook } from '@testing-library/react-hooks'
import useBar from './useBar'
describe('Replace `useEffect` with `useLayoutEffect` to check for an infinite loop', () => {
let useEffect = React.useEffect
beforeAll(() => {
React.useEffect = React.useLayoutEffect
})
afterAll(() => {
React.useEffect = useEffect
})
it('useBar', async () => {
const { result } = renderHook(() => useBar())
})
})
我有以下钩子:
const useBar = () => {
const [myFoo, setFoo] = useState(0);
const [myBar, setBar] = useState(0);
useEffect(() => {
setFoo(myFoo + 1);
console.log("setting foo (1)", myFoo, myBar);
}, [setFoo, myFoo, myBar]);
useEffect(() => {
setBar(myBar + 1);
console.log("setting bar (2)", myFoo, myBar);
}, [setBar, myBar, myFoo]);
};
使用组件时,我有预期的无限循环行为:
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
const Bar = () => {
useBar();
return <div>Bar</div>;
};
function App() {
return (
<Bar />
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
来自 console.log :
setting foo (1) 1 1
setting bar (2) 1 1
setting foo (1) 2 2
setting bar (2) 2 2
setting foo (1) 3 3
setting bar (2) 3 3
...
然而,使用 @testing-library/react-hooks
进行测试仅给出第一个循环输出:
describe("Tests", () => {
test("useBar", async () => {
const { rerender } = renderHook(() => {
return useBar();
});
// await waitForNextUpdate();
// tried the above doesn't work
// rerender();
// with rerender it loops through the next "cycle"
});
});
想知道如何正确测试挂钩,而不调用 rerender
并模仿与 React 相同的行为 - 在状态更改时重新渲染组件。
我猜你想测试一个使用useEffect
的hook
,当出现死循环时报错。
但实际上我们无法测试死循环,只能对次数进行限制。当循环超过这个限制时,我们认为它会无限循环下去。至于是否真的是无限循环,不好证明。
然而,要测试 useEffect
的重复并不容易。
虽然使用waitForNextUpdate
会出现超时错误,但是这个错误也可能是长时间的异步操作导致的,作为单元测试需要快速反馈。你不能每次都等待超时。这是浪费时间。
感谢这个问题,我找到了方向Provide a way to trigger useEffect from tests
当我用 useLayoutEffect
替换 useEffect
时,我可以快速准确地得到 Maximum update depth exceeded
错误。这只需要将要求的测试中的useEffect
替换为useLayoutEffect
即可。当循环无限时,测试将失败。
import React from 'react'
import { renderHook } from '@testing-library/react-hooks'
import useBar from './useBar'
describe('Replace `useEffect` with `useLayoutEffect` to check for an infinite loop', () => {
let useEffect = React.useEffect
beforeAll(() => {
React.useEffect = React.useLayoutEffect
})
afterAll(() => {
React.useEffect = useEffect
})
it('useBar', async () => {
const { result } = renderHook(() => useBar())
})
})