【问题标题】:How to test hooks with two useEffect statements?如何使用两个 useEffect 语句测试钩子?
【发布时间】:2019-11-27 08:30:51
【问题描述】:

我有以下钩子:


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 相同的行为 - 在状态更改时重新渲染组件。

【问题讨论】:

  • 您能否提供一个工作示例来说明您想要实现的目标?在您当前的示例中,您最终会得到一个无休止的更新“循环”,这会减慢 UI 的速度。我通过使用 act 助手实现了你正在做的事情的变体:codesandbox.io/s/ecstatic-ardinghelli-hsctu(请记住,这是一个人为的例子)。
  • 我不熟悉@testing-library/react-hooks,但正如西蒙所说,您是否尝试过使用来自react-dom/test-utilsact 构建它?
  • 我不明白你为什么要使用两个useEffect和next,为什么要将setBar函数放入数组依赖中?
  • @SimonIngeson 有许多实用的钩子,其中包括不止一个 useEffect。我把无限循环放在那里,因为有时你的钩子可能会因为这种效果而进入无限循环,而且你可能想测试这不会发生。另一方面,当使用react-hooks-testing-library 进行测试时,带有 2+ useEffect 的钩子无法正确执行,这就是我提出问题的原因。如果你觉得这个例子不合逻辑,请尝试思考:“How to test that a hook is not running an infinite loop, due to interdependent useEffect
  • @dinchev 是的,我知道,但在依赖数组中,您只能放置变量、对象、数组来进行浅层检查,是否需要重新运行渲染。你不能用函数进行浅层检查

标签: reactjs react-testing-library react-hooks-testing-library


【解决方案1】:

我猜你想测试一个使用useEffecthook 并在发生无限循环时报告错误。 但实际上我们无法测试无限循环,我们只能设置次数的限制。当循环超过这个限制时,我们认为它会继续无限循环。至于是否真的是无限循环,就不好证明了。

但是,要测试useEffect 的重复并不容易。 虽然使用waitForNextUpdate会出现超时错误,但是这个错误也可能是长时间异步操作导致的,作为单元测试需要快速反馈。您不能每次都等待超时。这是浪费时间。

感谢这个问题,我找到了方向。Provide a way to trigger useEffect from tests

当我将useEffect 替换为useLayoutEffect 时,我可以快速准确地得到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())
  })
})

【讨论】:

    猜你喜欢
    • 2020-09-14
    • 1970-01-01
    • 2020-10-23
    • 2021-12-26
    • 1970-01-01
    • 2020-01-26
    • 2020-01-11
    • 2020-03-20
    • 2020-02-19
    相关资源
    最近更新 更多