React 测试库在测试失败之前不会重新渲染元素,即使我在应该更新之后提前了计时器

问题描述 投票:0回答:1

我正在尝试测试一个组件,其值根据在一定时间后调用的回调的承诺结果而变化。

我的组件看起来像这样。我已经验证它在我的浏览器中按预期工作。

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

export interface TestComponentProps {
  callback: () => Promise<string>
}

const TestComponent: React.FC<TestComponentProps> = ({
  callback,
}: TestComponentProps) => {
  const [textDisplay, setTextDisplay] = useState('not resolved');
  const [timeLeft, setTimeLeft] = useState(1000);
  const [timerDone, setTimerDone] = useState(false);
  const timeStart = Date.now();

  const receiveText = async (text: Promise<string>) => {
    setTextDisplay(await text);
  }

  useEffect(() => {
    if (timerDone) {
      return () => undefined;
    }
    // Update screen every 50 ms
    const interval = setInterval(() => {
      const newTimeLeft = 1000 - Date.now() + timeStart;
      setTimeLeft(newTimeLeft);
      if (newTimeLeft <= 0) {
        setTimerDone(true);
        receiveText(callback());
      }
    }, 50);
    return () => clearInterval(interval);
  }, [timerDone]);

  if (!timerDone) {
    return (
    <p>{timeLeft} ms to go</p>
    );
  } else {
    return (
      <p>{textDisplay}</p>
    )
  }
};

export default TestComponent;

我的测试如下:

import { render, screen, act } from '@testing-library/react';
import React from 'react';
import TestComponent from './TestComponent';

it('renders the required information after the promise resolves', () => {
  jest.useFakeTimers();
  const callback = jest.fn().mockResolvedValue('Hello world');
  render(<TestComponent callback={callback} />);

  // Perform actions to update the state before waiting for the timer as required
  // ...

  // Now wait for the timer to finish - give it an extra 50ms just in case
  act(() => jest.advanceTimersByTime(1050));
  // The promise should have resolved and the text should have appeared in the document
  expect(screen.getByText('Hello world')).toBeInTheDocument();

  // The promise only resolves HERE
  // after the test will already have failed

  jest.clearAllTimers();
  jest.useRealTimers();
});

我不能

await new Promise(process.nextTick)
(根据这个问题),因为这与假计时器相结合会导致死锁。

我已经尝试按照

这个问题
使用waitFor(() => expect(callback).toBeCalled());,但它也未通过测试 - 我的代码中调用回调的断点永远不会被命中。

在使用假计时器时查找组件中的文本之前,如何确保 Promise 解析并重新渲染组件?

reactjs jestjs es6-promise react-testing-library
1个回答
1
投票

我可以通过使

act
回调异步并等待它来解决这个问题。我不知道为什么会这样,但确实如此。

这是固定的测试用例。编辑内容标有

HERE

import { render, screen, act } from '@testing-library/react';
import React from 'react';
import TestComponent from './TestComponent';

//                                                           HERE |
//                                                                V
it('renders the required information after the promise resolves', async () => {
  jest.useFakeTimers();
  const callback = jest.fn().mockResolvedValue('Hello world');
  render(<TestComponent callback={callback} />);

  // Perform actions to update the state before waiting for the timer as required
  // ...

  // Now wait for the timer to finish - give it an extra 50ms just in case
  //   HERE |
  //        V
  await act(async () => jest.advanceTimersByTime(1050));
  // The promise should have resolved and the text should have appeared in the document
  expect(screen.getByText('Hello world')).toBeInTheDocument();

  // The promise now resolves before HERE
  // meaning the test now passes!

  jest.clearAllTimers();
  jest.useRealTimers();
});
© www.soinside.com 2019 - 2024. All rights reserved.