Time com 87% de cobertura e deploy doloroso descreve um problema conhecido: o número subiu e a confiança da equipe não subiu junto.

Cobertura conta linhas executadas durante o teste. Ela não diz se o teste verifica o comportamento certo, se cobre o caso que quebra o negócio, ou se um refactor com bug passaria em silêncio. Você chega a 100% com uma suite que não pega nada útil. O guia de práticas de cobertura do Google Testing Blog lista os erros de leitura da métrica, e o bliki de TestCoverage de Martin Fowler descreve a cobertura como mapa do código que ficou sem teste.

A pirâmide de pé, com outro nome

O modelo clássico (muitos unit, poucos integration, menos E2E) continua válido no princípio. Na prática, times aplicam errado quando escrevem centenas de unit tests presos à implementação. Refatorou, a suite inteira quebra, e o time aprende que refatorar dói.

Kent C. Dodds resumiu o princípio que eu aplico: quanto mais o teste se parece com o uso do software, mais confiança ele entrega. Ele detalha o raciocínio em Write tests. Not too many. Mostly integration. O SMURF: Beyond the Test Pyramid do Google Testing Blog propõe categorizar teste por fidelidade e velocidade em vez de por camada, e descreve como o Google mede isso.

Comportamento na frente de implementação

// preso à implementação: quebra em qualquer refactor
it('chama setLoading(true), depois fetch, depois setLoading(false)', () => {
  const setLoading = jest.fn()
  // ...
})

// preso ao comportamento: sobrevive a refactor
it('mostra o spinner durante o carregamento e depois exibe os resultados', async () => {
  render(<ProductList />)

  expect(screen.getByRole('progressbar')).toBeInTheDocument()

  await screen.findByText('Produto A')

  expect(screen.queryByRole('progressbar')).not.toBeInTheDocument()
  expect(screen.getByText('Produto A')).toBeInTheDocument()
})

O segundo teste usa as queries de papel da React Testing Library, que pedem o que a pessoa vê na tela. Ele sobrevive a troca de estado, de método de fetch e de formato de cache.

Os casos que a suite precisa cobrir

Caminho feliz

Entrada válida produz a saída esperada. É o ponto de partida; sozinho, não protege o negócio.

Casos de borda

Entrada vazia, valor extremo, null onde a API promete número, string de dez mil caracteres. Bug de produção mora aqui.

Caminho de erro

API externa fora do ar, banco indisponível, usuário sem permissão. Sem teste nesse caminho, você descobre a tela de erro junto com o cliente.

Invariantes de negócio

Saldo não fica negativo. Usuário não lê dado de outro usuário. Pedido não aprova sem item. Esse teste é a regra de negócio escrita de forma que o CI executa.

Integration test entrega mais confiança por linha de código

Um integration test com banco de teste percorre o endpoint inteiro e cobre mais comportamento que dezenas de unit tests isolados.

describe('POST /orders', () => {
  it('cria o pedido e debita o estoque', async () => {
    await db.product.create({ id: 'p1', stock: 5 })

    const res = await request(app)
      .post('/orders')
      .set('Authorization', `Bearer ${userToken}`)
      .send({ productId: 'p1', quantity: 2 })

    expect(res.status).toBe(201)
    expect(res.body.order.status).toBe('confirmed')

    const product = await db.product.findUnique({ where: { id: 'p1' } })
    expect(product.stock).toBe(3)
  })
})

Um teste, cinco comportamentos: autenticação, validação, regra de pedido, débito de estoque e formato da resposta.

Três números que medem confiança

Substitua o consolidado de cobertura por:

  • Bugs em produção nos últimos três meses com a suite verde. Cada um deles aponta um buraco de comportamento que a cobertura não mostrou.
  • Horas gastas consertando teste que quebrou sem mudança de comportamento. É o custo de teste preso à implementação, pago em horas que o produto esperava em features.
  • Deploys em que alguém pediu para esperar o feriado passar. Esse número mede a confiança da equipe melhor que qualquer porcentagem.

Suite de 60% cobrindo comportamento de negócio protege mais que suite de 90% presa a implementação. O How Much Testing is Enough? do Google Testing Blog entra nessa conta de custo e benefício, e o post sobre testes frágeis da mesma fonte lista os padrões de API que evitam o acoplamento à implementação.