#javascript #reactjs #async-await #jestjs #react-testing-library
Вопрос:
У меня возникли проблемы с использованием библиотеки тестирования реакции для тестирования компонента переключения.
По щелчку значка (завернутого в компонент кнопки) я ожидаю, что текст перейдет от «проверено» к «непроверено». Кроме того, функция вызывается там, где происходят обновления состояния.
Однако событие щелчка, похоже, не работает, и я получаю следующую ошибку:
> jest "MyFile.spec.tsx"
FAIL src/my/path/__tests__/MyFile.spec.tsx
component MyFile
✓ renders when opened (94 ms)
✓ renders with items (33 ms)
✕ toggles verification status on click of icon button (100 ms)
console.error
Warning: An update to MyFile inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
This ensures that you're testing the behavior the user would see in the browser. Learn more at https://reactjs.org/link/wrap-tests-with-act
at MyFile (/path/to/myfile.tsx:44:3)
at ThemeProvider (/users/node_modules/@material-ui/styles/ThemeProvider/ThemeProvider.js:48:24)
123 | );
124 | } finally {
> 125 | setIsLoading(false);
| ^
126 | }
127 | };
128 |
at printWarning (node_modules/react-dom/cjs/react-dom.development.js:67:30)
at error (node_modules/react-dom/cjs/react-dom.development.js:43:5)
at warnIfNotCurrentlyActingUpdatesInDEV (node_modules/react-dom/cjs/react-dom.development.js:24064:9)
at dispatchAction (node_modules/react-dom/cjs/react-dom.development.js:16135:9)
at handleConfirm (src/modules/myfile.tsx:125:7)
В моем коде у меня есть такая функция:
const handleSubmit = async() => {
if(isLoading) {
return;
}
try {
setIsLoading(true);
await myFunctionCalls();
} catch (error){
console.log(error)
} finally {
setIsLoading(false)
}
};
Мой тест выглядит примерно так:
test('toggles verification status on click of icon button', async () => {
renderWithTheme(
<MyComponent/>,
);
const updateVerificationMock = jest.fn();
const callFunctionWithSerializedPayloadMock =
callFunctionWithSerializedPayload as jest.Mock;
callFunctionWithSerializedPayloadMock.mockImplementation(
() => updateVerificationMock,
);
const button = screen.getByRole('button', {name: 'Remove approval'});
fireEvent.click(button);
await act(async () => {
expect(myFunctionCalls).toHaveBeenCalledTimes(1);
});
expect(await screen.findByText('unverified')).toBeInTheDocument();
});
Первое ожидание проходит, так как вызовы функций вызываются один раз, однако у меня есть ошибка act() сверху, а также сбой, так как кажется, что текст не переключается с verified
на unverified
.
Я знаю, что обычно ошибка act связана с асинхронностью/ожиданием вызовов, но я подумал, что findByText должен подождать, и, похоже, есть еще одна проблема, которую я здесь не улавливаю. Любая помощь в том, что нужно сделать для отладки/улучшения этого теста?
Ответ №1:
Есть 3 асинхронные функции, которые вызываются здесь при нажатии на Remove Approval
кнопку.
Сначала вы устанавливаете состояние загрузки в значение true, поэтому оно загрузится, затем вызывается асинхронная функция (myFunctionCalls), и, наконец, загрузчик исчезнет после установки состояния загрузки в значение false.
Чтобы решить эту проблему, мы должны сначала дождаться появления загрузки, затем вызвать myFunctionCalls, а затем дождаться исчезновения загрузки.
test("toggles verification status on click of icon button", async () => {
renderWithTheme(<MyComponent />);
const updateVerificationMock = jest.fn();
const callFunctionWithSerializedPayloadMock =
callFunctionWithSerializedPayload as jest.Mock;
callFunctionWithSerializedPayloadMock.mockImplementation(
() => updateVerificationMock
);
const button = screen.getByRole("button", { name: "Remove approval" });
fireEvent.click(button);
expect(await screen.findByText(/loading/i)).toBeInTheDocument();
await waitFor(() => {
expect(myFunctionCalls).toHaveBeenCalledTimes(1);
});
await waitForTheElementToBeRemoved(() => {
expect(screen.queryByText(/loading/i)).not.toBeInTheDocument();
});
expect(await screen.findByText("unverified")).toBeInTheDocument();
});
Если у вас нет загружаемого текста, вы можете использовать act(() => jest.advanceTimersByTime(500));
его для продления времени до 500 мс. Когда время достигнет 500 мс, функция асинхронности будет решена.
beforeEach(() => {
jest.useFakeTimers();
})
afterEach(() => {
jest.runAllPendingTimers();
jest.useRealTimers()
})
test("toggles verification status on click of icon button", async () => {
renderWithTheme(<MyComponent />);
const updateVerificationMock = jest.fn();
const callFunctionWithSerializedPayloadMock =
callFunctionWithSerializedPayload as jest.Mock;
callFunctionWithSerializedPayloadMock.mockImplementation(
() => updateVerificationMock
);
const button = screen.getByRole("button", { name: "Remove approval" });
fireEvent.click(button);
act(() => jest.advanceTimersByTime(500));
await waitFor(() => {
expect(myFunctionCalls).toHaveBeenCalledTimes(1);
});
act(() => jest.advanceTimersByTime(500));
expect(await screen.findByText("unverified")).toBeInTheDocument();
});