🧑💻测试 React Hooks 的完整指南 🗓 + Demo 🍿
使用辅助方法优化测试
到目前为止,我们已经看到了如何完全测试我们的钩子。这种方法并不完美,但它有效。然而,我们能做得更好吗?
是的。请注意,我们为每次获取等待固定的 500 毫秒,但每个请求需要 200 到 500 毫秒。所以,我们显然是在浪费时间。我们可以通过等待每个请求花费的时间来更好地处理这个问题。
我们怎么做?一个简单的技术是执行断言,直到它通过或达到超时。让我们创建一个waitFor执行此操作的函数。
async function waitFor(cb, timeout = 500) {
const step = 10;
let timeSpent = 0;
let timedOut = false;
while (true) {
try {
await sleep(step);
timeSpent += step;
cb();
break;
} catch {}
if (timeSpent >= timeout) {
timedOut = true;
break;
}
}
if (timedOut) {
throw new Error("timeout");
}
}
此函数只是try...catch每 10 毫秒在块内运行一次回调 (cb) ,如果timeout达到,则会引发错误。这允许我们运行断言,直到它以安全的方式通过(即没有无限循环)。
我们可以在我们的测试中使用它,如下所示:我们使用我们的waitFor函数,而不是休眠 500 毫秒然后断言。
// INSTEAD OF
await act(() => sleep(500));
expect(container.textContent).toBe("url1");
// WE DO
await act(() =>
waitFor(() => {
expect(container.textContent).toBe("url1");
})
);
在所有此类断言中执行此操作,我们可以看到测试运行速度(代码)的显着差异。
现在,这一切都很棒,但也许我们不想通过 UI 测试钩子。也许我们想使用它的返回值来测试一个钩子。我们怎么做?
这并不难,因为我们已经可以访问钩子的返回值。它们就在组件内部。如果我们可以将这些变量放到全局范围内,它就会起作用。所以让我们这样做。
由于我们将通过其返回值而不是渲染 DOM 来测试我们的钩子,因此我们可以从组件中移除 HTML 渲染并使其渲染null。我们还应该删除 hook 返回中的解构以使其更通用。因此,我们有了这个更新的测试组件。
// global variable
let result;
function TestComponent({ url }) {
result = useStaleRefresh(url, defaultValue);
return null;
}
现在钩子的返回值存储在result一个全局变量中。我们可以查询它的断言。
// INSTEAD OF
expect(container.textContent).toContain("loading");
// WE DO
expect(result[1]).toBe(true);
// INSTEAD OF
expect(container.textContent).toBe("url1");
// WE DO
expect(result[0].data).toBe("url1");
在我们到处更改之后,我们可以看到我们的测试通过了(代码)。
至此,我们了解了测试 React Hooks 的要点。我们仍然可以进行一些改进,例如:
- 将
result变量移动到局部作用域 - 无需为我们要测试的每个钩子创建一个组件
我们可以通过创建一个包含测试组件的工厂函数来实现。它还应该在测试组件中呈现钩子并让我们可以访问result变量。让我们看看如何做到这一点。
首先,我们在函数内部移动TestComponent和result。我们还需要将 Hook 和 Hook 参数作为函数的参数传递,以便它们可以在我们的测试组件中使用。使用它,这就是我们所拥有的。我们正在调用这个函数renderHook。
function renderHook(hook, args) {
let result = {};
function TestComponent({ hookArgs }) {
result.current = hook(...hookArgs);
return null;
}
act(() => {
render(<TestComponent hookArgs={args} />, container);
});
return result;
}
我们result将数据存储在其中result.current的原因是因为我们希望在测试运行时更新返回值。我们的钩子的返回值是一个数组,所以如果我们直接返回它,它就会被值复制。通过将它存储在一个对象中,我们返回对该对象的引用,以便可以通过更新来更新返回值result.current。
现在,我们如何更新钩子?由于我们已经在使用闭包,让我们附上另一个rerender可以做到这一点的函数。
最终的renderHook函数如下所示:
function renderHook(hook, args) {
let result = {};
function TestComponent({ hookArgs }) {
result.current = hook(...hookArgs);
return null;
}
function rerender(args) {
act(() => {
render(<TestComponent hookArgs={args} />, container);
});
}
rerender(args);
return { result, rerender };
}
现在,我们可以在我们的测试中使用它。我们不使用actand render,而是执行以下操作:
const { rerender, result } = renderHook(useStaleRefresh, [
"url1",
defaultValue,
]);
然后,我们可以使用断言result.current和更新钩子rerender。这是一个简单的例子:
rerender(["url2", defaultValue]);
expect(result.current[1]).toBe(true); // check isLoading is true
在所有地方更改它后,您将看到它可以正常工作(代码)。
杰出的!现在我们有一个更清晰的抽象来测试钩子。我们仍然可以做得更好——例如,即使它没有改变,defaultValue每次也需要传递给rerender。我们可以解决这个问题。
但是我们不要太绕圈子,因为我们已经有了一个可以显着改善这种体验的库。
输入react-hooks-testing-library。
使用 React-hooks-testing-library 进行测试
React-hooks-testing-library 做了我们之前讨论过的所有事情,然后做了一些。例如,它处理容器安装和卸载,因此您不必在测试文件中执行此操作。这使我们可以专注于测试我们的钩子而不会分心。
它带有一个renderHook返回rerender和的函数result。它还返回wait,它类似于waitFor,因此您不必自己实现它。
这是我们在 React-hooks-testing-library 中渲染钩子的方式。注意钩子是以回调的形式传递的。每次测试组件重新渲染时都会运行此回调。
const { result, wait, rerender } = renderHook(
({ url }) => useStaleRefresh(url, defaultValue),
{
initialProps: {
url: "url1",
},
}
);
然后,我们可以通过这样做来测试第一次渲染的结果是否isLoading为真并返回值defaultValue。与我们上面实现的完全相似。
expect(result.current[0]).toEqual(defaultValue);
expect(result.current[1]).toBe(true);
为了测试异步更新,我们可以使用返回的wait方法renderHook。它带有包裹,act()所以我们不需要包裹act()它。
await wait(() => {
expect(result.current[0].data).toEqual("url1");
});
expect(result.current[1]).toBe(false);
然后,我们可以使用rerender新的 props 来更新它。注意我们不需要通过defaultValue这里。
rerender({ url: "url2" });
最后,其余的测试将类似地进行(代码)。
包起来
我的目的是通过一个异步钩子的例子向你展示如何测试 React Hooks。我希望这可以帮助您自信地处理任何类型的钩子的测试,因为相同的方法应该适用于大多数钩子。
我建议您使用 React-hooks-testing-library,因为它已经完成,到目前为止我还没有遇到重大问题。如果您确实遇到问题,您现在知道如何使用本文中描述的测试钩子的复杂性来解决它。
文章链接:https://www.lilianhua.com/%f0%9f%8d%bf%f0%9f%a7%91%f0%9f%92%bb-complete-guide-to-test-react-hooks-%f0%9f%97%93-demo-%f0%9f%8d%bf.html
English (US)
Español (ES)
Português (PT)
Français (CA)
Español (MX)
Español (VE)
Español (CO)
Español (AR)
Português (BR)
Quechua (PE)
Guaraní (PY)
简体中文 (ZH)
繁體中文 (HK)
日本語 (JP)
한국어 (KR)
हिन्दी (HI)
Pilipino (PH)
ไทย (TH)
Tiếng Việt (VN)
Bahasa Melayu (MY)
Bahasa Indonesia (ID)
বাংলা (BD)
اردو (PK)
සිංහල (LK)
ភាសាខ្មែរ (KH)
English (UK)
Français (FR)
Deutsch (DE)
Italiano (IT)
Русский (RU)
Nederlands (NL)
Türkçe (TR)
Polski (PL)
Svenska (SE)
Norsk (NO)
Dansk (DK)
Suomi (FI)
Ελληνικά (GR)
Čeština (CZ)
Magyar (HU)
Română (RO)
Български (BG)
Српски (RS)
Українська (UA)


