异步 thunk 会在测试中导致警告(包含在 act 中)

kar*_*sos 3 redux react-redux react-testing-library redux-toolkit

当尝试测试调度异步 thunk 的组件时,我收到以下警告。它们是由于测试完成后执行的更新而显示的。

  console.error
    Warning: An update to App 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 https://reactjs.org/link/wrap-tests-with-act
        at App (/home/karlosos/Dev/nokia/playground/testing-redux/src/App.tsx:6:34)
        at Provider (/home/karlosos/Dev/nokia/playground/testing-redux/node_modules/react-redux/lib/components/Provider.js:19:3)
        at Wrapper (/home/karlosos/Dev/nokia/playground/testing-redux/src/testUtils.tsx:11:22)

      at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
      at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
      at warnIfUpdatesNotWrappedWithActDEV (node_modules/react-dom/cjs/react-dom.development.js:27589:9)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:25508:5)
      at forceStoreRerender (node_modules/react-dom/cjs/react-dom.development.js:16977:5)
      at Object.handleStoreChange [as callback] (node_modules/react-dom/cjs/react-dom.development.js:16953:7)
      at node_modules/react-redux/lib/utils/Subscription.js:23:20
Run Code Online (Sandbox Code Playgroud)

这就是警告可见的原因。 在此输入图像描述

修复它们的一种方法是引入某种屏障,该屏障将等待所有挂起的异步操作完成。但是这样做我的测试需要对我不想测试的逻辑进行断言。

在此输入图像描述

我在这里重新创建了一个最小的可重现项目:https ://github.com/karlosos/react-redux-async-warnings/tree/main/src

我的示例测试如下所示:

  console.error
    Warning: An update to App 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 https://reactjs.org/link/wrap-tests-with-act
        at App (/home/karlosos/Dev/nokia/playground/testing-redux/src/App.tsx:6:34)
        at Provider (/home/karlosos/Dev/nokia/playground/testing-redux/node_modules/react-redux/lib/components/Provider.js:19:3)
        at Wrapper (/home/karlosos/Dev/nokia/playground/testing-redux/src/testUtils.tsx:11:22)

      at printWarning (node_modules/react-dom/cjs/react-dom.development.js:86:30)
      at error (node_modules/react-dom/cjs/react-dom.development.js:60:7)
      at warnIfUpdatesNotWrappedWithActDEV (node_modules/react-dom/cjs/react-dom.development.js:27589:9)
      at scheduleUpdateOnFiber (node_modules/react-dom/cjs/react-dom.development.js:25508:5)
      at forceStoreRerender (node_modules/react-dom/cjs/react-dom.development.js:16977:5)
      at Object.handleStoreChange [as callback] (node_modules/react-dom/cjs/react-dom.development.js:16953:7)
      at node_modules/react-redux/lib/utils/Subscription.js:23:20
Run Code Online (Sandbox Code Playgroud)

和例子thunk:

  test('WHEN component rendered THEN counter value is being loaded', () => {
    // WHEN
    renderWithProviders(<App />)

    // THEN
    expect(Api.getValue).toHaveBeenCalledTimes(1);
    const loadingSpinner = screen.getByTestId('loading-spinner');
    expect(loadingSpinner).toBeInTheDocument();

    // things will happen to the component here after test is done
    // precisely the data fetched from API will be displayed
  });
Run Code Online (Sandbox Code Playgroud)

waitFor更新 1 -测试结束时使用时没有警告

当我await waitFor(() => new Promise(res => setTimeout(res, 0)));在测试结束时添加时,警告不可见。但我不想编辑每个测试用例。这看起来像是一个黑客。

export const fetchCounterValue = (): AppThunk => async (dispatch, getState) => {
  if (getState().counter.fetchValueStatus === "loading") {
    return;
  }

  dispatch(fetchValueStart());
  try {
    const result = await Api.getValue();
    dispatch(fetchValueSuccess(result));
  } catch (e) {
    dispatch(fetchValueError('Could not fetch the data'));
  }
};
Run Code Online (Sandbox Code Playgroud)

kar*_*sos 5

正确的解决方案不是设置global.IS_REACT_ACT_ENVIRONMENT = false;,而是首先制作正确的测试用例。此act(...) 警告意味着测试完成后组件发生了一些更新。

\n

在这个特定场景中,这是由于加载微调器消失然后显示数据造成的。为了修复这个测试场景,我们需要等待这个加载旋转器消失或者等待数据出现在屏幕上。

\n

正确的测试用例应该是这样的:

\n
  test(\'WHEN component rendered THEN counter value is being loaded\', async () => {\n    // WHEN\n    renderWithProviders(<App />)\n\n    // THEN\n    expect(Api.getValue).toHaveBeenCalledTimes(1);\n    const loadingSpinner = screen.getByTestId(\'loading-spinner\');\n    expect(loadingSpinner).toBeInTheDocument();\n\n    await waitForElementToBeRemoved(() => screen.queryByTestId(\'loading-spinner\')); // This is preventing `act(...)` warnings\n  });\n
Run Code Online (Sandbox Code Playgroud)\n

在此处了解有关act(...)警告的更多信息:

\n\n