让单元测试等待Asynchronous Fetch填充的数据

Make Unit Test Wait for Data filled by Asynchronous Fetch

我有一个单元测试,其中包括渲染一个使用 useSWR 获取数据的组件。但是在调用 expect() 之前数据还没有准备好,所以测试失败。

test("StyleOptionDetails contains correct style option number", () => {
    renderWithMockSwr(
        <StyleOptionDetails
            {...mockIStyleOptionDetailsProps}
        />
    )
    expect(screen.getByText(123)).toBeInTheDocument();
});

但如果我延迟 setTimeout(),它会通过测试。

setTimeout(() => {
    console.log('This will run after 2 second')
    expect(screen.getByText(123)).toBeInTheDocument();
}, 2000);

创建延迟或等待数据的正确方法是什么?

我在其他地方对 ReactJS 问题有类似的回答,。让我针对您的问题制定解决方案。

如果您的函数 renderWithMockSwr() 是异步的,那么如果您希望它在调用下一行之前等待完成执行,请使用 await 命令。

await renderWithMockSwr(
    <StyleOptionDetails
        {...mockIStyleOptionDetailsProps}
    />
)

async太棒了。 await也是。看看:Mozilla Developer Network: Async Function

虽然我认为你已经在这样做了,但首先要注意的是你不应该实际上获取任何数据从你的测试中——你应该模拟结果。

一旦你这样做了,你将使用 waitFor 实用程序来帮助你进行异步测试——这个实用程序基本上接受一个 returns 期望的函数 (expect) , 并将保持在测试的那个点,直到达到预期。

让我们举个例子。以我在下面创建的虚构组件为例:

const MyComponent = () => {
    const [data, setData] = useState();
    useEffect(() => {
        MyService.fetchData().then((response) => setData(response));
    }, []);

    if (!data) {
        return (<p>Loading...</p>);
    }
    // else
    return (
        <div>
            <h1>DATA!</h1>
            <div>
                {data.map((datum) => (<p>{datum}</p>))}
            </div>
        </div>
    );
}

所以对于你的测试,你会做

import MyService from './MyService';
import MyComponent from './MyComponent';

describe('MyComponent', () => {
    const mockData = ['Spengler', 'Stanz', 'Venkman', 'Zeddmore'];
    beforeEach(() => {
        jest.spyOn(MyService, 'fetchData')
            .mockImplementation(
                () => new Promise((res) => setTimeout(() => res(mockData), 200))
            );
    });
    afterEach(() => {
        MyService.fetchData.mockRestore();
    });
    it('displays the loading first and the data once fetched', async () => {
        render(<MyComponent />);
        // before fetch completes
        expect(screen.getByText(/Loading/)).toBeInTheDocument();
        expect(screen.queryByText('DATA!')).toBeNull();
        // after fetch completes..
        // await waitFor will wait here for this to be true; if it doesn't happen after about five seconds the test fails
        await waitFor(() => expect(screen.getByText('DATA!')).toBeInTheDocument());
        expect(screen.queryByText(/Loading/)).toBeNull(); // we don't have to await this one because we awaited the proper condition in the previous line
    });
});

这没有经过测试,但像这样的东西应该可以工作。您的模拟方法可能会因您构建提取的方式而有所不同。