React钩子单元测试30秒搞定!

990 阅读2分钟

自React引入钩子函数以来,我们多了一个总结共享逻辑的选项——自定义钩子。阿里的ahooks是一个很棒的总结分享自定义钩子的例子。

问题也随之而来,怎么测试来确保它的稳定性?

你说它就是个函数,对于函数我们测它的输入输出不就好了嘛!

没毛病!然后我们就看到这样的错误:

react hooks testing invalid

钩子必须在一个React组建中运行。为了避免这个错误,我们要么把react和react-dom给mock了,要么设法包个傀儡组件用于测试。各种头疼。

幸好社区有解决方案——@tesing-library/react-hooks。假设我们有一个简单的useCounter()钩子函数:

import { useState, useCallback } from "react";

export default function useCounter() {
  const [counter, setCounter] = useState(0);
  const increase = useCallback(() => setCounter(counter + 1), [counter]);
  return {
    counter,
    increase,
  };
}

测试counter 状态的变化只需要两步:

  1. renderHook()渲染一个测试用的组件,让钩子函数可以正常跑起来;
  2. act()方法中调用钩子函数提供的操作方法,让react重新渲染组件。
import useCounter from "./useCounter";
import { renderHook, act } from "@testing-library/react-hooks";

test("basic hook rendering", () => {
  const { result } = renderHook(() => useCounter());
  expect(result.current).toMatchInlineSnapshot(`
    Object {
      "counter": 0,
      "increase": [Function],
    }
  `);
  act(() => result.current.increase());
  expect(result.current.counter).toBe(1);
});

然后如常断言测试。

如果遇到异步操作,则与《React组件单元测试之交互》一文中介绍的waitFor()方法雷同,需多加一步等待响应发生:

import useCounter from "./useCounter";
import { renderHook, act } from "@testing-library/react-hooks";


test("async hook rendering - waitFor", async () => {
  const { result, waitFor } = renderHook(() => useCounter());
  act(() => result.current.increase());
  await waitFor(() => expect(result.current.counter).toBe(1));
});
test("async hook rendering - waitForNextUpdate", async () => {
  const { result, waitForNextUpdate } = renderHook(() => useCounter());
  act(() => result.current.increase());
  await waitForNextUpdate();
  expect(result.current.counter).toBe(1);
});
test("async hook rendering - waitForValueToChange", async () => {
  const { result, waitForValueToChange } = renderHook(() => useCounter());
  act(() => result.current.increase());
  await waitForValueToChange(() => result.current.counter);
  expect(result.current.counter).toBe(1);
});

以上例子中提供了3个选项:

  • waitFor
  • waitForNextUpdate
  • waitForValueToChange

分别可以应用于不同的测试目标。

是不是非常简便?

让我们愉快地写测试,看看其他测试系列文: