`act` vs. `waitFor`

Iniciado por joomlamz, 25 de Maio de 2026, 15:00

Respostas: 1   |   Visualizações: 15

Tópico anterior - Tópico seguinte

0 Membros e 1 Visitante estão a ver este tópico.

Olá, pessoal! Hoje vamos mergulhar no mundo da programação e discutir sobre dois conceitos importantes: `act` e `waitFor`. Esses dois métodos são frequentemente utilizados em testes automatizados e desenvolvimento de software, mas muitas vezes são confundidos ou mal utilizados.

Em resumo, `act` é um método que executa uma ação específica em um componente ou sistema, enquanto `waitFor` é um método que pausa a execução do teste até que uma condição específica seja atendida. Em outras palavras, `act` é usado para realizar uma ação, enquanto `waitFor` é usado para aguardar que algo aconteça.

Um exemplo prático é quando estamos testando uma aplicação web que carrega dados de uma API. Nesse caso, podemos usar `act` para simular a ação de clicar em um botão que dispara a requisição à API, e em seguida usar `waitFor` para aguardar que os dados sejam carregados e exibidos na tela.

A escolha entre `act` e `waitFor` depende do contexto e do objetivo do teste. Se estamos testando a interação do usuário com a aplicação, `act` pode ser a escolha certa. No entanto, se estamos testando a integração com uma API ou um serviço externo, `waitFor` pode ser mais apropriado.

Agora, vamos abrir o debate! Quais são as suas experiências com `act` e `waitFor`? Como vocês utilizam esses métodos em seus projetos? Compartilhem suas histórias e dicas no fórum webmastersmz.com!

E, para garantir que os vossos projetos e fóruns rodam sem falhas, convido-vos a conhecer as soluções de alojamento de alta performance da AplicHost em https://aplichost.com. Com a AplicHost, você pode ter certeza de que seus projetos estão em boas mãos, com suporte técnico especializado e infraestrutura de ponta. Então, não percam mais tempo e visitem o site da AplicHost hoje mesmo!

`act` vs. `waitFor`



Tópico: `act` vs. `waitFor`
Categoria: Tutoriais | Programação & Tecnologia
Idioma Principal: Português (Conteúdo de Tecnologia)

Descrição do Conteúdo / Informações:
-------------------------------------------------------------------------


Introduction


After reviewing posts about @testing-library/react on forums, it became clear that some developers struggle to understand what act does (and doesn't do), when to use it, what the root cause of that annoying warning is, and when to use act vs waitFor. This blog post aims to address that.



act


From the React docs,

`act` is a test helper to apply pending
React updates before making assertions.

The act function from @testing-library/react is a simple wrapper around the act function from React.

The name of the function comes from the Arrange-Act-Assert pattern of writing tests. act serves to prevent tests from asserting on incomplete renders before all updates have finished.

The React docs also recommend using act with await and an async function callback.

Let's demonstrate the use of act by writing a test for a simple custom hook.

// useCounter.ts
import { useState } from 'react';

export function useCounter() {
const [count, setCount] = useState(0);
const increment = () => setCount((c) => c + 1);

return { count, increment };
}

// useCounter.test.tsx
test("counter increments (Fails without act)", () => {
const { result } = renderHook(() => useCounter());

// Throws the "not wrapped in act(...)" warning
result.current.increment();

// ❌ Test fails
expect(result.current.count).toBe(1);
});

Here's an example of how act would be used to solve this:

// useCounter.test.tsx
test("counter increments (Passes with act)", async() => {
const { result } = renderHook(() => useCounter());

// Wrap the direct state-changing method in act
await act(async () => {
result.current.increment();
});

// React has now flushed the updates safely
// ✅ Test passes
expect(result.current.count).toBe(1);
});

So, the callback passed to act will be executed first and then React updates will be flushed before the test continues. Failure to wrap the action in act could result in React state changes taking place after the assertion runs, which results in the dreaded warning:

Warning: An update to Counter 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 results */

This ensures that the behavior matches how React works in the browser.
at Counter



What act isn't


I believe the confusion comes from the phrase "React updates" and I've found that the simplest way to clear things up is to identify what doesn't count as a "React update". Here are two of the most common browser APIs that aren't "React updates", and so aren't directly controlled by act.

Fetch requests

// UserProfile.tsx
import React, { useState, useEffect } from 'react';

export function UserProfile() {
const [name, setName] = useState<string>('Loading...');

useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users/1')
.then((response) => response.json())
.then((data) => {
setName(data.name);
})
.catch(() => {
setName('Failed to load user');
});
}, []);

return (
<div>
<h1>User Profile</h1>
<p data-testid="user-name">{name}</p>
</div>
);
}

// UserProfile.test.tsx
test("proving act cannot block or control real network requests", () => {
// Uses `act` internally, so we don't need to wrap this in `act()`
render(<UserProfile />);

const nameDisplay = screen.getByTestId("user-name");

// ❌ Test fails
expect(nameDisplay).not.toHaveTextContent("Loading...");
});

Fetch requests are sent to the networking thread, which is outside of React's scheduling system. So, invoking act won't immediately resolve the request. This test fails because when the assertion is executed, the network request is still pending. So, the DOM is still in its initial state.

Timers

// DelayedMessage.tsx
import { useEffect, useState } from 'react';

export function DelayedMessage() {
const [message, setMessage] = useState('Waiting...');

useEffect(() => {
setTimeout(() => {
setMessage('Hello World!');
}, 1000);
}, []);

return <p data-testid="message">{message}</p>;
}

// DelayedMessage.test.tsx
test("proving act cannot fast-forward native setTimeouts", () => {
render(<DelayedMessage />);

const messageDisplay = screen.getByTestId("message");

// ❌ Test fails
expect(messageDisplay.textContent).not.toBe("Waiting...");
});

This test fails because timers aren't controlled by React. So, act cannot force the clock to jump forward by default.

"React updates" are things like state updates and effects, not browser or runtime APIs, so understanding which APIs are provided by the browser or runtime and which are provided by React can save you time and energy.

Mocking is part of the solution for working with non-React APIs, but mocking alone is often not enough. The next section will show some simple approaches for handling external APIs.



waitFor


The waitFor function is used to retry any callback until it stops throwing. It's typically used to wait for assertions to pass without knowing when they'll be ready. Use it for asynchronous side-effects and network requests. The waitFor function repeatedly executes the callback passed to it, until either the callback succeeds or the timeout is exceeded.

await waitFor(() => {
expect(messageDisplay.textContent).toBe("Hello World!");
});

You also have the option to configure the timeout and delay between retries.

await waitFor(
() => {
expect(messageDisplay.textContent).toBe("Hello World!");
},
{
timeout: 3000, // Tells RTL to keep retrying for up to 3 seconds before failing
interval: 100   // Optional: Checks the DOM every 100ms (default is 50ms)
}
);

Like render, waitFor also wraps act. Query methods, such as findBy*, wrap waitFor. getBy* and queryBy* don't. This will explain why you don't see waitFor and act in many tests.

Here's how we can use mocking and waitFor to fix the tests shown earlier:

For fetch requests:

// UserProfile.test.tsx
test("renders user name after mock fetch resolves", async () => {
// Mock global fetch before rendering the component
const mockFetch = vi.fn().mockResolvedValue({
json: () => Promise.resolve({ name: "John Doe" }),
});
vi.stubGlobal("fetch", mockFetch);

render(<UserProfile />);

// Uses `waitFor` internally, so we don't need to wrap this in `waitFor()`
const nameDisplay = await screen.findByText("John Doe");

// ✅ Test passes
expect(nameDisplay).toBeInTheDocument();
expect(nameDisplay).not.toHaveTextContent("Loading...");

// Clean up the global mock after the test finishes
vi.unstubAllGlobals();
});

And for timers like setTimeout:

// DelayedMessage.test.tsx
test("renders message after fake timers advance", async () => {
// Tell Vitest to hijack the native browser clock
vi.useFakeTimers();

render(<DelayedMessage />);

const messageDisplay = screen.getByTestId("message");

// Verify the initial state before the timer fires
expect(messageDisplay).toHaveTextContent("Waiting...");

// Advancing time by 1000ms triggers a React state update,
// so this action must be wrapped in act().
await act(async () => {
vi.advanceTimersByTime(1000);
});

// ✅ Test passes
expect(messageDisplay).not.toHaveTextContent("Waiting...");
expect(messageDisplay).toHaveTextContent("Hello World!");

// Clean up and restore the real system clock
vi.useRealTimers();
});

Note: The examples use vi from vitest, but the same principles apply to tests written with jest.



Conclusion


If you get confused about when and where to use act or waitFor, it helps to remember the following rules of thumb:

• If we're performing an action and we require React updates to complete before proceeding, use act (or a function that wraps it).

• If we're waiting for an async update, or if we need to retry a condition until it becomes true, use waitFor (or a function that wraps it).

• If the update relies on a non-React API, act may not be enough. Consider mocking.



Sources


• React docs - act

• Testing-library/react docs - act

• Testing-library/user-event docs



Further Reading


• Kent's blog post on the act warning


Joomlamz
Consultoria em Informática
-------------------------------------------------------
Especialista em Sistemas Web & Manutenção de Servidores.
A desenvolver o novo AplPortal com suporte a PHP 8.
Precisa de ajuda profissional? Contacte-me.

Tags: