Vamos falar a real? Boa parte dos testes de front-end hoje são… inúteis. Eles quebram por qualquer mudança visual, não pegam bugs reais e viram custo, não valor. E o pior: dão falsa sensação de segurança.
Se você quer jogar no nível sênior, precisa mudar o jeito de testar.
Esse guia é direto ao ponto: 7 passos práticos para criar testes que realmente fazem sentido.
1. Pare de testar implementação (e comece a testar comportamento)
Esse é o erro mais comum.
❌ Testando implementação
import { render } from '@testing-library/react';
import Button from './Button';
test('deve renderizar botão com classe primary', () => {
const { container } = render(<Button />);
expect(container.firstChild).toHaveClass('btn-primary');
});
Problema:
👉 qualquer refactor quebra o teste
👉 mesmo sem alterar comportamento
✅ Testando comportamento
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Button from './Button';
test('deve disparar ação ao clicar', async () => {
const handleClick = jest.fn();
render(<Button onClick={handleClick}>Enviar</Button>);
await userEvent.click(screen.getByText('Enviar'));
expect(handleClick).toHaveBeenCalled();
});
Agora você testa o que importa:
👉 o que o usuário faz
2. Evite snapshot testing (na maioria dos casos)
Snapshot parece bonito.
Mas na prática…
👉 vira um dump gigante impossível de revisar
❌ Snapshot inútil
import { render } from '@testing-library/react';
test('snapshot do componente', () => {
const { container } = render(<Card />);
expect(container).toMatchSnapshot();
});
Problema real
- ninguém revisa snapshot
- qualquer mudança quebra
- dev só dá “update snapshot”
👉 virou ritual, não validação
Quando usar snapshot?
- componentes muito estáveis
- estruturas críticas (raros casos)
Fora isso…
👉 evite sem dó
3. Teste fluxos reais (não funções isoladas)
Usuário não usa função.
Usuário usa fluxo.
❌ Teste artificial
test('função soma', () => {
expect(sum(2, 2)).toBe(4);
});
Legal…
Mas isso não garante nada na UI.
✅ Teste de fluxo real
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import Login from './Login';
test('usuário consegue fazer login', async () => {
render(<Login />);
await userEvent.type(screen.getByLabelText('Email'), 'teste@email.com');
await userEvent.type(screen.getByLabelText('Senha'), '123456');
await userEvent.click(screen.getByRole('button', { name: 'Entrar' }));
expect(await screen.findByText('Bem-vindo')).toBeInTheDocument();
});
Agora sim:
👉 você validou o que realmente importa
4. Use testes E2E para o que realmente importa
Ferramentas como Playwright e Cypress entram aqui.
Exemplo com Playwright
import { test, expect } from '@playwright/test';
test('fluxo de compra completo', async ({ page }) => {
await page.goto('http://localhost:3000');
await page.click('text=Produtos');
await page.click('text=Adicionar ao carrinho');
await page.click('text=Finalizar compra');
await expect(page.locator('h1')).toHaveText('Pedido confirmado');
});
Quando usar E2E
- fluxos críticos (checkout, login)
- integração entre páginas
- validação real do sistema
Quando NÃO usar
- testar cada botão
- testar lógica isolada
👉 E2E é caro — use com inteligência
5. Testes de contrato: o segredo pouco usado
Esse aqui é nível sênior.
Problema
Frontend depende da API.
Se backend muda…
👉 tudo quebra
Solução: contrato
// contrato esperado
type User = {
id: number;
name: string;
};
Testando contrato
import { fetchUsers } from './api';
test('API retorna usuários no formato correto', async () => {
const users = await fetchUsers();
expect(users).toEqual(
expect.arrayContaining([
expect.objectContaining({
id: expect.any(Number),
name: expect.any(String),
}),
])
);
});
👉 você garante que backend não quebrou seu front
6. Mock com responsabilidade (sem virar mentira)
Mock é útil.
Mas também é perigoso.
❌ Mock enganoso
jest.mock('./api', () => ({
fetchUsers: () => Promise.resolve([{ id: 1, name: 'Fake' }]),
}));
👉 você está testando um mundo que não existe
✅ Mock mais realista
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/users', (req, res, ctx) => {
return res(
ctx.json([
{ id: 1, name: 'Rafa' },
{ id: 2, name: 'Dev' },
])
);
})
);
beforeAll(() => server.listen());
afterAll(() => server.close());
👉 simula comportamento real da API
7. Teste menos, mas melhor
Esse é o passo mais difícil.
Anti-pattern clássico
- 300 testes
- lentos
- frágeis
- ninguém confia
Abordagem sênior
- poucos testes
- focados em fluxo
- confiáveis
- fáceis de manter
Estratégia prática
Se você quiser algo equilibrado:
- Testes unitários → lógica crítica
- Testes de integração → componentes
- E2E → fluxos principais
O que realmente diferencia um sênior
Não é saber ferramenta. É saber o que NÃO testar, porque no fim:
👉 teste bom pega bug
👉 teste ruim só ocupa tempo
Conclusão: confiança, não cobertura
Cobertura alta não significa qualidade.
O que importa é: confiança no deploy, segurança para refatorar e bugs pegos antes da produção.
Se seus testes não fazem isso… eles não estão cumprindo o papel deles.




