我正在尝试测试一个组件,其值根据在一定时间后调用的回调的承诺结果而变化。
我的组件看起来像这样。我已经验证它在我的浏览器中按预期工作。
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 解析并重新渲染组件?
我可以通过使
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();
});