Jest 无法在未安装的组件上执行 React 状态更新 + React 状态更新应该包装到 act 中

Jest can't perform a React state update on an unmounted component + react state updates should be wrapped into act

所以我正在尝试测试表单输入。首先,表单应显示错误,然后在输入值后,用户单击按钮,错误应被删除。
我想我明白为什么会这样,但我不知道如何测试这个功能。

我有条件地呈现错误消息并更新状态,所以我知道这可能就是它告诉我将测试包装在 act() 中的原因。我还告诉模拟服务器发送错误状态,我想也许因为我覆盖了初始处理程序,它只会响应我定义的错误状态。那也不行。

由于我是测试新手,所以我不确定如何进行测试。非常感谢任何帮助。

这里是测试函数:

it('clears validation error after username field is updated', async () => {
  let validationError;
  server.use(generateValidationError('username', 'Username cannot be null'));
  setup();

  await act(async () => {
    userEvent.click(button);
    validationError = await screen.findByText('Username cannot be null');
    userEvent.type(usernameInput, 'username001');
    userEvent.click(button);
    expect(validationError).not.toBeInTheDocument();
  });
});

由于在组件中设置状态或异步执行某些操作可能需要“一些时间”,因此您应该将您的情况视为潜在的类似 promise 的情况。

要确保您的测试始终等待正在发生的任何更改,您可以使用 waitFor 方法并在代码中添加回调函数以检查某些状态。这是来自React Testing library

it('clears validation error after username field is updated', async () => {
  let validationError;
  server.use(generateValidationError('username', 'Username cannot be null'));
  setup();

  await act(async () => {
    userEvent.click(button);
    userEvent.type(usernameInput, 'username001');
    userEvent.click(button);

    await waitFor(async () => {
      validationError = await screen.findByText('Username cannot be null');
      expect(validationError).not.toBeInTheDocument();
    });
  });
});

提示
当您断言包含 DOM 元素的内容时,在执行某些操作后始终 select 该元素(在单击按钮后更改您的案例中的验证状态)。

在上面的代码中,我改变了 select 或关于上述提示的位置。

在方法上,我会:

  1. 测试错误初步可见
  2. 点击按钮
  3. 测试错误已消失

如果您要使用 act(),通常会将断言放在其函数体之外,请参阅 https://reactjs.org/docs/testing-recipes.html#act

我什至不确定你是否需要 act()。官方测试库文档中的示例似乎可以满足您的需求:

真是个愚蠢的错误。 我遇到的第一个错误是因为我在成功提交后隐藏了我的表单。所以该组件将卸载,然后我将无法对其进行测试。我通过分别测试每个字段并确保它没有成功提交表单来找到解决方法。

通过修复上述错误,我不再需要使用 act() 了,因为我没有收到任何开玩笑的错误。之后测试顺利通过。

it.each`
      field         | message                      | label
      ${'username'} | ${'Username cannot be null'} | ${'Username'}
      ${'email'}    | ${'Email cannot be null'}    | ${'Email'}
      ${'password'} | ${'Password cannot be null'} | ${'Password'}
    `(
      'clears validation error after $field field is updated',
      async ({ field, message, label }) => {
        server.use(generateValidationError(field, message));
        setup();
        userEvent.click(button);
        const validationError = await screen.findByText(message);
        const inputByLabel = screen.getByLabelText(label);
        userEvent.type(
          inputByLabel,
          label === 'Email' ? 'user@testuser.com' : 'username001'
        );
        //this line was needed to stop the form from submitting
        userEvent.type(confirmPasswordInput, 'newpassword');
        userEvent.click(button);
        await waitFor(() => {
          expect(validationError).not.toBeInTheDocument();
        });
      }
    );