Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Як тестувати асинхронну поведінку: findBy, waitFor і попередження про act?

Багато змін в інтерфейсі відбуваються не одразу: після відповіді сервера, таймера, переходу (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: асинхронні методи

Схожі питання