2

我有一个单元测试,其中包括渲染一个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);

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

4

2 回答 2

2

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

完成此操作后,您将使用该waitFor实用程序来帮助您进行异步测试——该实用程序基本上接受一个返回期望 ( 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
    });
});

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

于 2021-09-13T15:11:56.170 回答
1

对于 ReactJS 问题, Web Fetch API(等待获取完成然后执行下一条指令),我在其他地方也有类似的答案。让我为您的问题制定解决方案。

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

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

async太棒了。也是如此await。看看:Mozilla 开发者网络:异步函数

于 2021-09-13T14:40:26.047 回答