为什么我需要在行为中包装 React/Enzyme 状态变化?

jar*_*gon 10 reactjs jestjs react-dom enzyme

在我的简单 React/ReactDOM/Enzyme 单元测试中,我收到来自 ReactDOM 的警告,关于将任何突变包装到act(). 如果我的测试仍然通过,为什么我需要这样做?我有 50 个左右的 React 组件,它们都使用钩子、自定义钩子等。我从不换行act(),它们都通过了。

我可以禁用此警告吗?我不想无缘无故地添加额外的代码。

警告:

Warning: An update to MyComponent inside a test was not wrapped in act(...).

    When testing, code that causes React state updates should be wrapped into act(...):

    act(() => {
      /* fire events that update state */
    });
    /* assert on the output */

    This ensures that you're testing the behavior the user would see in the browser. Learn more at [redacted]
Run Code Online (Sandbox Code Playgroud)

我的测试:

const App = () => {
  const [isLoaded, setIsLoaded] = useState(false);

  const myOnClick = () => {
    setIsLoaded(true);
  };

  return (
    <div onClick={myOnClick}>
      {isLoaded ? 'Yes' : 'No'}
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)
describe('My test', () => {
  let wrapper

  beforeAll(() => {
    wrapper = mount(<App />)
  })

  it('renders "no"', () => {
    expect(wrapper.text()).toBe('No')
  })

  describe('When onClick is called', () => {
    beforeAll(() => {
      wrapper.find('div').prop('onClick')()
    })

    it('renders "yes"', () => {
      expect(wrapper.text()).toBe('Yes')
    })
  })
})
Run Code Online (Sandbox Code Playgroud)

CodeSandbox 重现:https ://codesandbox.io/s/react-169-act-warning-repro-olm8o ? expanddevtools =1& fontsize =14& hidenavigation =1& previewwindow = tests

sky*_*yer 8

act()首先运行所有相关的useEffect,否则将以异步方式运行。您看不到任何区别,因为您useEffect的组件代码中没有

根据最新版本的Enzyme 文档mount()应该已经在act()内部包装了:

如果您使用的是 React 16.8+ 和.mount(),Enzyme 将包装包括.simulate(), .setProps(), .setContext(), .invoke()with在内的 api ,ReactTestUtils.act()因此您无需手动包装它。

我们不能在 Enzyme 内部将.prop()(或.props())的结果包装起来,.act()因为它会破坏返回值的相等性。但是,您可以使用.invoke()来简化代码:

const wrapper = mount(<SomeComponent />);
wrapper.invoke('handler')();
expect(/* ... */);```
Run Code Online (Sandbox Code Playgroud)


小智 5

引起警告的行act()

wrapper.find('div').prop('onClick')()
Run Code Online (Sandbox Code Playgroud)

正如 @skyboyer 的回答所建议的那样,内部prop没有包装act。当您调用 onClick 并更新状态时,React 会警告某些内容发生意外更改。如果您使用.simulate,则不需要将其用act.

wrapper.find('div').simulate('click')
Run Code Online (Sandbox Code Playgroud)