Багато змін в інтерфейсі відбуваються не одразу: після відповіді сервера, таймера, переходу (startTransition). Синхронна перевірка відразу після дії їх не побачить.
findBy... - чекає, поки елемент з'явиться (за замовчуванням до 1000 мс, перевіряючи кожні 50 мс):
test('завантажує вакансії', async () => {
render(<VacancyList />);
expect(screen.getByText('Завантаження...')).toBeInTheDocument();
expect(await screen.findByRole('heading', { name: 'Laravel-розробник' })).toBeInTheDocument();
});
findBy = waitFor + getBy, і це найпростіший спосіб дочекатися появи елемента.
waitFor - повторює колбек, поки він не перестане кидати помилку:
await user.click(screen.getByRole('button', { name: 'Зберегти' }));
await waitFor(() => expect(saveMock).toHaveBeenCalledWith({ title: 'Нова' }));
Правила для waitFor:
- одна перевірка всередині - якщо кілька, при падінні незрозуміло, яка не дочекалася;
- без побічних ефектів у колбеку: він виконується багато разів (клік у
waitForклацне кілька разів); - для появи елемента -
findBy, а неwaitFor(() => getBy...); - для зникнення -
waitForElementToBeRemoved(() => screen.queryByText('Завантаження...')).
Попередження «not wrapped in act(...)» означає: стан компонента оновився після того, як тест уже щось перевірив, і поза контролем тестових утиліт. Типові причини:
- запит завершився після закінчення тесту - тест не дочекався кінцевого стану. Рішення - дочекатися через
findByтого, що з'являється наприкінці; - таймер чи проміс оновлює стан, а тест не знає про це.
Не варто обгортати все в act вручну. render, userEvent, fireEvent, findBy, waitFor уже використовують act. Явний act потрібен рідко - наприклад, при виклику функції з renderHook чи прямому оновленні зовнішнього сховища.
Фальшиві таймери (vi.useFakeTimers()) прискорюють тести з затримками, але з userEvent потрібен параметр advanceTimers, а findBy/waitFor у сучасних версіях Testing Library самі просувають фальшиві таймери Jest; з Vitest це залежить від налаштування - простіше уникати фальшивих таймерів там, де можна.
Ознака поганого тесту - довільні затримки (await new Promise((r) => setTimeout(r, 500))): повільно й нестабільно.
Докладніше в документації: Testing Library: асинхронні методи