警告:测试中对 App 的更新未包含在酶和钩子中的 act(...) 中

Posted

技术标签:

【中文标题】警告:测试中对 App 的更新未包含在酶和钩子中的 act(...) 中【英文标题】:Warning: An update to App inside a test was not wrapped in act(...) in enzyme and hooks 【发布时间】:2020-12-13 16:00:43 【问题描述】:

我已经编写了这个组件。它使用钩子和状态获取数据。获取后,加载状态将更改为 false 并显示侧边栏。

我在使用 Jest 和 Enzyme 时遇到了问题,因为它确实在我的单元测试中对 Act 发出了警告。一旦我将行为添加到我的笑话和酶中,测试就失败了!

// @flow
import React,  useEffect, useState  from 'react';
import Sidebar from '../components/Sidebar';
import fetchData from '../apiWrappers/fetchData';

const App = () => 
  const [data, setData] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => 
    const getData = async () => 
      try 
        const newData = await fetchData();
        setData(newData);
        setLoading(false);
      
      catch (e) 
        setLoading(false);
      
    ;
    getData();
    // eslint-disable-next-line
  , []);
  return (
    <>
      !loading
        ? <Sidebar />
        : <span>Loading List</span>
    </>
  );
;
export default App;

而且,我添加了一个这样的测试,效果很好。

import React from 'react';
import  mount  from 'enzyme';
import fetchData from '../apiWrappers/fetchData';
import data from '../data/data.json';
import App from './App';

jest.mock('../apiWrappers/fetchData');

const getData = Promise.resolve(data);
fetchData.mockReturnValue(getData);

describe('<App/> Rendering using enzyme', () => 
  beforeEach(() => 
    fetchData.mockClear();
  );

  test('After loading', async () => 
    const wrapper = mount(<App />);
    expect(wrapper.find('span').at(0).text()).toEqual('Loading List');

    const d = await fetchData();
    expect(d).toHaveLength(data.length);

    wrapper.update();
    expect(wrapper.find('span').exists()).toEqual(false);
    expect(wrapper.html()).toMatchSnapshot();
  );
);

所以,我收到了警告:

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 */
);

我确实使用 act react-dom/test-utils 解决了这样的警告。

import React from 'react';
import  act  from 'react-dom/test-utils';
import  mount  from 'enzyme';
import fetchData from '../apiWrappers/fetchData';
import data from '../data/data.json';
import App from './App';

jest.mock('../apiWrappers/fetchData');

const getData = Promise.resolve(data);
fetchData.mockReturnValue(getData);

describe('<App/> Rendering using enzyme', () => 
  beforeEach(() => 
    fetchData.mockClear();
  );

  test('After loading', async () => 
    await act(async () => 
      const wrapper = mount(<App />);
      expect(wrapper.find('span').at(0).text()).toEqual('Loading List');

      const d = await fetchData();
      expect(d).toHaveLength(data.length);

      wrapper.update();
      expect(wrapper.find('span').exists()).toEqual(false);
      expect(wrapper.html()).toMatchSnapshot();
    );
  );
);

但是,我的测试失败了。

<App/> Rendering using enzyme › After loading

expect(received).toEqual(expected) // deep equality

Expected: false
Received: true

  35 | 
  36 |       wrapper.update();
> 37 |       expect(wrapper.find('span').exists()).toEqual(false);

有人知道为什么会失败吗?谢谢!

"react": "16.13.1",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.3",

【问题讨论】:

哇,那里的运行代码 sn-ps 的问题很好!为此投票! 【参考方案1】:

这个问题一点也不新鲜。你可以在这里阅读完整的讨论:https://github.com/enzymejs/enzyme/issues/2073。

总而言之,目前为了修复act 警告,您必须稍等片刻才能更新您的包装器,如下所示:

const waitForComponentToPaint = async (wrapper) => 
  await act(async () => 
    await new Promise(resolve => setTimeout(resolve));
    wrapper.update();
  );
;

test('After loading', async () => 
  const wrapper = mount(<App />);
  expect(wrapper.find('span').at(0).text()).toEqual('Loading List');
  
  // before the state updated
  await waitForComponentToPaint(wrapper);
  // after the state updated

  expect(wrapper.find('span').exists()).toEqual(false);
  expect(wrapper.html()).toMatchSnapshot();
);

【讨论】:

感谢您的评论,我实际上已经阅读了链接。而且,我刚刚尝试了您的代码。但是,即使在使用代码之后,我也会收到相同的警告。绕过它的唯一方法是将我的整个测试包装成这样的行为。 test.only('After loading', async () =&gt; await act(async () =&gt; /* the actual test */ );); 但这太过分了!!我在&lt;App /&gt; 中使用useEffect,这可能是个问题。我已经看到了这条评论github.com/enzymejs/enzyme/issues/2073,但是为我实现它很复杂。你有什么其他建议可以在不包装我的每一个测试的情况下完成吗? 尝试增加超时时间await new Promise(resolve =&gt; setTimeout(resolve, 20)); 看看是否有帮助? 或者你也可以尝试将上面的代码包装在act()我猜 我已经尝试将时间增加到 100,但仍然收到警告。如果我将上面的代码包装在行为中,我会得到另一个警告。到目前为止它工作的唯一方法是这样的:stackblitz.com/edit/react-act-warning-with-enzyme?file=src/… 但是,我不想对每一个测试都这样做,它有点太多而且有点混乱。你知道无论如何我可以简化它吗? :) 我认为我们不一定要使用 wait-for-expect 来使其工作 :) 你是否有可以重现问题的最小代码(可能是一个小仓库)?

以上是关于警告:测试中对 App 的更新未包含在酶和钩子中的 act(...) 中的主要内容,如果未能解决你的问题,请参考以下文章

使用酶和主题测试样式化组件

警告:无法在 React 本机中对未安装的组件执行 React 状态更新

[Vue 警告]:指令中的错误 intersect unbind 钩子:“TypeError:无法读取未定义的属性‘observer’”

使用上下文和钩子更新未安装组件的状态 - 反应原生

Vue警告:创建的钩子出错:“TypeError:无法读取未定义的属性'get'”

SVN中对repo中的某一目录设置钩子