Voltar ao blog
Tutorial

Testes automatizados frontend: o mínimo que funciona

Por Flávio Emanuel · · 8 min de leitura

Pulei testes por anos. Achava que era pra gente grande, agência com 100 devs. Aí perdi cliente porque site quebrou e ninguém percebeu até cliente reclamar.

Comecei a fazer teste. Mas achava Cypress pesado demais, Jest demorado. Testava manual e levava muito tempo.

Aí descobri Vitest + Playwright. Setup rápido, testes rodavam rápido, e mais importante: eu escrevia menos código.

Testes não precisam ser coisa de perfeição. Precisa ser coisa de segurança. Você dorme tranquilo sabendo que formulário funciona.

Por que devs solo pulam testes

Tempo. Projeto com prazo curto, você foca em feature. Teste fica pra depois e nunca vem.

Falta de hábito. Ninguém te ensina no bootcamp da forma certa. Aprende é testar manual mesmo.

Falso dilema. “Ou faço teste ou entrego rápido”. Mas testes salvam tempo depois. Um teste bem feito cai 10x o tempo que levou pra escrever quando precisa mudar algo.

Começar é chato. Configurar ferramentas, entender sintaxe, primeira vez é chato mesmo.

Resposta: comece pequeno. Não testa tudo. Testa o que importa.

O que testar, o que não testar

NÃO teste CSS pixel-perfect. Browser faz isso.

NÃO teste bibliotecas. Shadcn já vem testado. Tailwind também.

NÃO teste integração com terceiro de forma apertada. Tipo teste real chamando API real. Mocka a API.

SIM teste formulários. Se formulário quebra, cliente não consegue se cadastrar.

SIM teste navegação. Se rota quebra, usuário fica perdido.

SIM teste integração de dados. Se integração com Supabase quebra, site não funciona.

SIM teste lógica. Cálculos, validações, conversões de dados.

Vitest pra unit tests

Vitest é Jest mas rápido. Muito mais rápido. Configuração é mínima se você tá com Astro.

npm install -D vitest @testing-library/react happy-dom

vitest.config.ts:

import { getViteConfig } from 'astro/config'

export default getViteConfig({
  test: {
    globals: true,
    environment: 'happy-dom'
  }
})

Pronto. Vitest tá rodando.

Test de validação simples:

import { describe, it, expect } from 'vitest'
import { validateEmail } from './validate'

describe('validateEmail', () => {
  it('should accept valid email', () => {
    expect(validateEmail('user@example.com')).toBe(true)
  })

  it('should reject invalid email', () => {
    expect(validateEmail('invalid')).toBe(false)
  })

  it('should reject empty', () => {
    expect(validateEmail('')).toBe(false)
  })
})

Rode npm run test. Tá testando.

Test de componente:

import { render, screen } from '@testing-library/react'
import { Button } from './Button'

describe('Button', () => {
  it('renders with text', () => {
    render(<Button>Click me</Button>)
    expect(screen.getByText('Click me')).toBeInTheDocument()
  })

  it('calls onClick when clicked', () => {
    const onClick = vi.fn()
    render(<Button onClick={onClick}>Click me</Button>)
    screen.getByText('Click me').click()
    expect(onClick).toHaveBeenCalled()
  })
})

Tá testando componente. Se componente quebra, teste falha.

Playwright pra e2e

End-to-end é quando você testa de verdade. Abre navegador, clica, testa fluxo.

npm install -D @playwright/test
npx playwright install

playwright.config.ts:

import { defineConfig } from '@playwright/test'

export default defineConfig({
  webServer: {
    command: 'npm run dev',
    port: 3000,
    reuseExistingServer: !process.env.CI,
  },
  testDir: './e2e',
  use: {
    baseURL: 'http://localhost:3000',
  },
})

Test básico:

import { test, expect } from '@playwright/test'

test('appointment form submission', async ({ page }) => {
  await page.goto('/appointment')

  await page.fill('input[name="name"]', 'John Doe')
  await page.fill('input[name="email"]', 'john@example.com')
  await page.fill('input[name="date"]', '2026-05-01')

  await page.click('button:has-text("Schedule")')

  await expect(page).toHaveURL('/confirmation')
  await expect(page.locator('h1')).toContainText('Success')
})

Rodou? Seu formulário funciona.

Test de navegação:

test('navigation between pages', async ({ page }) => {
  await page.goto('/')

  await page.click('a:has-text("Services")')
  await expect(page).toHaveURL('/services')

  await page.click('a:has-text("Contact")')
  await expect(page).toHaveURL('/contact')
})

Setup prático pra 20 minutos por projeto

Não faça teste pra tudo. Faça teste pra:

  1. Formulário principal (contato, agendamento)
  2. Navegação
  3. Integração crítica (se puxa dados de Supabase, testa)

Isso é 5-7 testes e2e. Leva 20 minutos pra escrever.

Rode antes de fazer deploy. Se algo quebra, vê ali.

package.json:

{
  "scripts": {
    "test": "vitest",
    "test:e2e": "playwright test",
    "test:ci": "vitest run && playwright test"
  }
}

Por que isso salva tempo

Você termina projeto. Manda pra cliente. Cliente usa. Dois meses depois você precisa adicionar feature nova.

Sem teste, você mexe no código e quebra algo invisível. Descobre só quando cliente reclama.

Com teste, você roda teste. Se algo quebrou, você vê antes de cliente ver.

Um teste bem feito leva 15 minutos pra escrever e salva 5 horas de debug depois.

Integração contínua

Se tá no GitHub, adicione test em CI.

.github/workflows/test.yml:

name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run test:ci

Agora, todo push roda teste. Se algo quebra, PR avisa.

Realidade

Não precisa ter 100% cobertura. Precisa ter confiança pra mudar código.

Se você tá dormindo preocupado que site pode quebrar qualquer hora, comece a testar.

Se você tá feliz mandando pra produção sem testar, talvez seu projeto seja muito simples. Ou talvez você tenha sorte. Sorte acaba.

Leia também: MVP do zero ao deploy | Entregar projetos rápido com IA | Supabase + React

  • Instalar Vitest e configurar com Astro
  • Criar testes de validação (email, dados)
  • Testar componentes críticos (Button, Form)
  • Instalar e configurar Playwright
  • Escrever testes e2e de formulário
  • Testar navegação principal
  • Rodar testes antes de deploy

Teste não é luxo. É tranquilidade pra dormir.

Porque você esquece de testes no começo

Você aprende a programar, ninguém te força a testar. Bootcamp? Sim, tem aula de teste. Mas na prática, você só sai quando termina feature. Teste fica de lado.

Depois que você perde cliente porque site quebrou, aí muda de ideia.

Mas tem custo cognitivo alto. Você tá cansado de codar. Escrever teste é mais código. Cérebro diz “depois”.

Depois nunca chega.

Resposta: começa agora. Não com 100% de cobertura. Começa com 10% que importa.

Como seu workflow muda com testes

Antes: escreve feature, testa manual, deploy Depois: escreve feature, roda suite de tests, vê que funciona, deploy

Segundo fluxo leva 5 minutos mais. Mas economiza horas de debug 6 meses depois quando cliente pede mudança.

Você tá fazendo deploy pra cliente toda semana. Se 1 em 10 deploys quebra algo invisível, você descobrir caro. Com tests, descobrir barato.

Escalando testes pra múltiplos projetos

Começou com 2 testes e2e. Projeto cresce. Adiciona mais rota, mais formulário. Logo tem 20 testes.

Playwright aguenta. Leva 45 segundos pra rodar suite inteira. Continua rápido.

Agora tem 3 projetos ativos. Cada um com 15-20 testes. Roda tudo em CI, PR não mergea se tests falham. Tranquilidade.

Testes como documentação

Um bom teste é documentação viva. Alguém que nunca viu seu código lê seu teste e entende o fluxo.

test('user can schedule appointment', async ({ page }) => {
  // Usuário vai pra página de agendamento
  await page.goto('/schedule')

  // Preenche nome
  await page.fill('input[name="name"]', 'Maria Silva')

  // Seleciona data disponível
  await page.click('button[data-date="2026-05-15"]')

  // Confirma agendamento
  await page.click('text=Confirm Appointment')

  // Deve redirecionar pra página de confirmação
  await expect(page).toHaveURL('/confirmation')
})

Teste é história do que usuário faz. Documentação automática.

Métricas importantes

Cobertura é métrica enganadora. 80% de cobertura pode significar que tá cobrindo código que não importa.

Melhor métrica: “qual é a chance de um bug chegar em produção?” Se tá perto de zero pra fluxos críticos, tá bom.

Outro bom indicador: “quanto tempo leva pra debugar quando algo quebra?” Com testes, descobre rápido. Sem testes, quebra silencioso por dias.

Quando parar de testar

Não teste UI library. Shadcn já vem testado. Não teste packages. Zod já vem testado.

Teste código que você escreve. Lógica sua. Integração sua.

Teste rota crítica. Formulário de contato, fluxo de pagamento, autenticação.

Deixa testes banais de lado. “Botão renderiza” é banalidade.

Teste coisas que se quebram silenciosamente. “Dado de user vem vazio” é silencioso. Testa.

Realidade após 8 meses com testes

No começo, parecer que tá gastando tempo. 30 minutos por projeto em setup + testes.

Mês 2: um cliente pediu mudança. Rodei tests. Quebrou teste de formulário. Achei bug antes de cliente ver.

Mês 8: um cliente pediu integração com novo sistema. Mudei bastante código. Tests correram. Nada quebrou. Cliente feliz.

Quantidade de bugs em produção? Caiu 95%.

Tempo de debug? Caiu 80%.

Confiança em refatorar? Subiu 1000%.

Vale muito a pena.

Próximo passo

Precisa de um dev que entrega de verdade?

Seja pra um projeto pontual, reforço no time, ou parceria de longo prazo. Vamos conversar.

Falar no WhatsApp

Respondo em até 2h durante horário comercial.