🧑‍💻测试 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 的要点。我们仍然可以进行一些改进,例如:

  1. result变量移动到局部作用域
  2. 无需为我们要测试的每个钩子创建一个组件

我们可以通过创建一个包含测试组件的工厂函数来实现。它还应该在测试组件中呈现钩子并让我们可以访问result变量。让我们看看如何做到这一点。

首先,我们在函数内部移动TestComponentresult。我们还需要将 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,因为它已经完成,到目前为止我还没有遇到重大问题。如果您确实遇到问题,您现在知道如何使用本文中描述的测试钩子的复杂性来解决它。

温馨提示 : 非特殊注明,否则均为©李联华的博客网原创文章,本站文章未经授权禁止任何形式转载;IP地址:3.144.48.72,归属地:俄亥俄州Dublin ,欢迎您的访问!
文章链接: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
Popup Image

通知

本站原则上是免费提供技术支持,但是服务器维护和运营成本高,可以实行自由赞助:赞助

Loading...