O que é Testing Library?
Testing Library é uma família de bibliotecas que ajudam você a testar componentes de UI da maneira que um usuário os experimenta. Em vez de acessar as entranhas de um componente, você o renderiza, encontra os elementos como uma pessoa faria — por seu role, label ou texto visível — e interage com eles através de eventos realistas.
A ideia central é uma reação aos testes baseados em detalhes de implementação. Abordagens mais antigas renderizavam um componente e então faziam asserções sobre o estado interno, props ou uma estrutura específica do DOM. Esses testes quebravam a cada refatoração, enquanto capturavam pouquíssimos bugs reais. Testing Library inverte a perspectiva: se o usuário consegue ver e usar, você consegue testar, e o teste sobrevive a mudanças na forma como o componente funciona internamente.
Renderização e consultas
Você renderiza um componente e, em seguida, consulta o DOM resultante através de screen.
// LoginForm.test.jsx
import { render, screen } from "@testing-library/react";
import { LoginForm } from "./LoginForm";
test("shows the email field", () => {
render(<LoginForm />);
expect(screen.getByLabelText("Email")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "Sign in" })).toBeInTheDocument();
});
screen é a superfície de consulta recomendada. Ela pesquisa todo o documento renderizado, o que reflete a forma como um usuário vê a página, em vez de focar em um container específico.
Escolhendo a query certa
A Testing Library fornece diversas queries, e a ordem de prioridade importa. Prefira aquelas que estão mais próximas da perspectiva do usuário:
- getByRole — botões, links, cabeçalhos, inputs e mais, correspondidos pelo nome acessível. Esta é a melhor opção padrão.
- getByLabelText — controles de formulário encontrados por seu label.
- getByPlaceholderText — quando o placeholder é o único label disponível.
- getByText — conteúdo de texto não interativo.
- getByDisplayValue — o valor atual de um elemento de formulário.
- getByAltText — imagens pelo seu texto alternativo (alt text).
- getByTitle e getByTestId — últimos recursos.
// queries.jsx
screen.getByRole("heading", { level: 1, name: "Dashboard" });
screen.getByLabelText("Password");
screen.getByText("Welcome back");
screen.getByAltText("Company logo");
screen.getByTestId("chart");
getByRole é poderosa porque serve também como uma verificação de acessibilidade. Se um controle não possui um nome acessível, a query falha — o que significa que sua marcação provavelmente também é inacessível.
Cada query possui variantes: getBy lança um erro se nada for correspondido, queryBy retorna null, e findBy retorna uma promise que aguarda. Use queryBy para afirmar que algo está ausente, e findBy após uma atualização assíncrona.
Interagindo com userEvent
userEvent simula a sequência completa de eventos que um usuário real gera.
// Search.test.jsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Search } from "./Search";
test("submits the query", async () => {
const onSearch = vi.fn();
render(<Search onSearch={onSearch} />);
await userEvent.type(screen.getByLabelText("Search"), "vitest");
await userEvent.click(screen.getByRole("button", { name: "Go" }));
expect(onSearch).toHaveBeenCalledWith("vitest");
});
userEvent gerencia eventos de foco, teclado e ponteiro em conjunto, o que captura bugs que um único fireEvent.change deixaria passar. Prefira utilizá-lo para tudo, exceto nos raros casos em que ele não cubra a necessidade.
Comportamento assíncrono
A maioria dos componentes é atualizada de forma assíncrona, seja por meio de um fetch, um timer ou uma transição. Use findBy e waitFor em vez de delays arbitrários.
// Users.test.jsx
test("renders users after loading", async () => {
render(<Users />);
expect(screen.getByText("Loading…")).toBeInTheDocument();
const items = await screen.findAllByRole("listitem");
expect(items).toHaveLength(3);
});
findBy aguarda por um elemento correspondente e falha com uma mensagem útil caso ele nunca apareça. waitFor serve para aguardar por uma asserção ou uma condição que não seja apenas a busca por um elemento. Nunca utilize timeouts reais; eles tornam os testes lentos e instáveis (flaky).
Asserts com jest-dom
O pacote complementar @testing-library/jest-dom adiciona matchers que tornam a leitura do estado do DOM mais natural.
// matchers.test.jsx
expect(button).toBeDisabled();
expect(input).toHaveValue("[email protected]");
expect(dialog).not.toBeInTheDocument();
expect(heading).toHaveTextContent("Dashboard");
expect(link).toHaveAttribute("href", "/docs");
Esses matchers deixam a intenção mais clara do que verificações brutas de propriedades do DOM e geram mensagens de erro melhores.
Testes com foco em acessibilidade
Como o getByRole depende de roles e nomes acessíveis, escrever testes dessa forma revela problemas de acessibilidade precocemente. Se você não consegue encontrar um botão por role, usuários de leitores de tela também não conseguirão. Adicionar getByRole e toHaveAccessibleName aos seus testes é uma das maneiras mais baratas de melhorar a acessibilidade e, frequentemente, substitui a necessidade de uma auditoria automatizada separada.
Testing Library em diferentes frameworks
A biblioteca possui adaptadores para React, Vue, Svelte, Angular e outros. A API de query é compartilhada, portanto, o modelo mental é transferível mesmo que a configuração seja diferente. O adaptador para React é o mais utilizado e o que será demonstrado aqui.
Melhores práticas
- Faça a busca primeiro por role, depois por label e então por texto; use test ids apenas como último recurso.
- Use
userEventem vez defireEvent. - Aguarde por
findByewaitForem vez de utilizar delays. - Faça asserções sobre o que o usuário vê, não sobre o estado interno.
- Mantenha os testes focados em apenas um comportamento cada.
- Faça a limpeza automaticamente entre os testes; o Testing Library já faz isso por padrão.
- Trate um
getByRolefalho como uma dica de que existe um problema de acessibilidade.
Erros comuns
- Recorrer ao
container.querySelectore testar nomes de classes. - Usar
fireEventquandouserEventcapturaria mais casos. - Esperas arbitrárias com
setTimeoutem vez defindBy. - Tirar snapshots de toda a árvore renderizada.
- Fazer asserções no estado ou props do componente em vez de no DOM.
- Adicionar test ids em tudo e perder os benefícios de acessibilidade.
Próximos passos
A Testing Library é a camada de componentes de uma estratégia de testes sólida. Execute-a com Vitest ou Jest, aprofunde seus conhecimentos em React para que os componentes sejam testáveis e cubra jornadas completas com Playwright. Agora, escolha um componente que você já criou e escreva um teste sob a perspectiva do usuário.