如何解决testing-library-react中的“update was not returned in act()”警告?

Pab*_*rde 37 unit-testing reactjs jestjs react-testing-library testing-library

我正在使用一个有副作用的简单组件。我的测试通过了,但我收到警告Warning: An update to Hello inside a test was not wrapped in act(...).

我也不知道这是否waitForElement是编写此测试的最佳方法。

我的组件

export default function Hello() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
      setPosts(response.data);
    }

    fetchData();
  }, []);

  return (
    <div>
      <ul>
        {
          posts.map(
            post => <li key={post.id}>{post.title}</li>
          )
        }
      </ul>
    </div>
  )
}

Run Code Online (Sandbox Code Playgroud)

我的组件测试

import React from 'react';
import {render, cleanup, act } from '@testing-library/react';
import mockAxios from 'axios';
import Hello from '.';

afterEach(cleanup);

it('renders hello correctly', async () => {
  mockAxios.get.mockResolvedValue({
    data: [
        { id: 1, title: 'post one' },
        { id: 2, title: 'post two' },
      ],
  });

  const { asFragment } = await waitForElement(() => render(<Hello />));

  expect(asFragment()).toMatchSnapshot();
});
Run Code Online (Sandbox Code Playgroud)

sli*_*wp2 32

更新的答案:

请参阅下面的@mikaelrs 评论。

不需要waitFor 或waitForElement。你可以只使用 findBy* 选择器,它返回一个可以等待的承诺。例如 await findByTestId('list');


弃用的答案:

使用waitForElement是正确的方法,来自文档:

等到get模拟的请求承诺解决并且组件调用 setState 并重新渲染。waitForElement等到回调不抛出错误

这是您案例的工作示例:

index.jsx

import React, { useState, useEffect } from 'react';
import axios from 'axios';

export default function Hello() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const fetchData = async () => {
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
      setPosts(response.data);
    };

    fetchData();
  }, []);

  return (
    <div>
      <ul data-testid="list">
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}
Run Code Online (Sandbox Code Playgroud)

index.test.jsx

import React from 'react';
import { render, cleanup, waitForElement } from '@testing-library/react';
import axios from 'axios';
import Hello from '.';

jest.mock('axios');

afterEach(cleanup);

it('renders hello correctly', async () => {
  axios.get.mockResolvedValue({
    data: [
      { id: 1, title: 'post one' },
      { id: 2, title: 'post two' },
    ],
  });
  const { getByTestId, asFragment } = render(<Hello />);

  const listNode = await waitForElement(() => getByTestId('list'));
  expect(listNode.children).toHaveLength(2);
  expect(asFragment()).toMatchSnapshot();
});
Run Code Online (Sandbox Code Playgroud)

100% 覆盖率的单元测试结果:

 PASS  stackoverflow/60115885/index.test.jsx
  ? renders hello correctly (49ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 index.jsx |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 passed, 1 total
Time:        4.98s
Run Code Online (Sandbox Code Playgroud)

index.test.jsx.snapshot

// Jest Snapshot v1

exports[`renders hello correctly 1`] = `
<DocumentFragment>
  <div>
    <ul
      data-testid="list"
    >
      <li>
        post one
      </li>
      <li>
        post two
      </li>
    </ul>
  </div>
</DocumentFragment>
`;
Run Code Online (Sandbox Code Playgroud)

源代码:https : //github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60115885

  • 顺便说一句,“waitForElement”现已弃用。他们建议 `import { waitFor } from '@testing-library/react'` 。很好的答案! (10认同)
  • 不需要“waitFor”或“waitForElement”。您可以只使用“findBy*”选择器,它返回一个可以等待的承诺。例如 `await findByTestId('list'); ...` (6认同)
  • 如果我不关心更新后显示的内容,而只是断言是否使用正确的数据调用发出服务器请求的函数,该怎么办? (4认同)

小智 21

我有一个错误:

console.error
  Warning: A suspended resource finished loading inside a test, but the event was not wrapped in act(...).
  
  When testing, code that resolves suspended data should be wrapped into act(...):
  
  act(() => {
    /* finish loading suspended data */
  });
  /* 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
Run Code Online (Sandbox Code Playgroud)

代码:

test('check login link', async () => {
    renderRouter({ initialRoute: [home.path] });
    const loginLink = screen.getByTestId(dataTestIds.loginLink);
    expect(loginLink).toBeInTheDocument();
  
    userEvent.click(loginLink);
    const emailInput = screen.getByTestId(dataTestIds.emailInput);
    expect(emailInput).toBeInTheDocument();
}
Run Code Online (Sandbox Code Playgroud)

我已经解决了:

test('check login link', async () => {
  renderRouter({ initialRoute: [home.path] });
  const loginLink = screen.getByTestId(dataTestIds.loginLink);
  expect(loginLink).toBeInTheDocument();

  userEvent.click(loginLink);

  await waitFor(() => {
    const emailInput = screen.getByTestId(dataTestIds.emailInput);
    expect(emailInput).toBeInTheDocument();
  });
}
Run Code Online (Sandbox Code Playgroud)

我刚刚包裹在回调 fn - waitFor() 中

也许对某人有用


Kar*_*umi 14

WaitFor为我工作,我尝试使用findByTestId此处提到的方法,但我仍然遇到相同的操作错误。

\n

我的解决方案:

\n
it('Should show an error message when pressing \xe2\x80\x9cNext\xe2\x80\x9d with no email', async () => {\nconst { getByTestId, getByText  } = render(\n  <Layout buttonText={'Common:Actions.Next'} onValidation={() => validationMock}\n  />\n);\n\nconst validationMock: ValidationResults = {\n  email: {\n    state: ValidationState.ERROR,\n    message: 'Email field cannot be empty'\n  }\n};\n\nawait waitFor(() => {\n  const nextButton = getByTestId('btn-next');\n  fireEvent.press(nextButton);\n});\n\nexpect(getByText('Email field cannot be empty')).toBeDefined();\n
Run Code Online (Sandbox Code Playgroud)\n