Pular para o conteúdo

Boas práticas e padrões · Qualidade e testes · 6 min de leitura

Pirâmide de testes e TDD

Muitos testes unitários rápidos, alguns de integração e poucos de ponta a ponta — e o ciclo vermelho, verde, refatorar.

Vermelho → Verde1Verde → Refatorar2Refatorar → Vermelho: próximo casopróximo caso3Vermelho — teste que falhaVermelhoteste que falhaVerde — código mínimoVerdecódigo mínimoRefatorar — limpar com segurançaRefatorarlimpar com segurançaPoucos E2E — lentos, frágeisPoucos E2Elentos, frágeis

Arraste para o lado para ver o diagrama inteiro.

Cada volta do ciclo leva minutos: um teste pequeno falhando, o código mínimo para passar, e a limpeza com a rede de testes verde.

A pirâmide

  • Base — unitários: milhares, em milissegundos, sem rede nem disco. Regras de negócio, cálculos, ViewModels com fakes.
  • Meio — integração: repositório + banco real em memória, cliente HTTP contra servidor fake.
  • Topo — ponta a ponta (E2E): poucos fluxos críticos (login, compra) na UI real. Lentos e mais frágeis.

A inversão (muitos E2E, poucos unitários) é o "cone de sorvete": suíte lenta e instável.

TDD

Kent Beck: escreva o teste antes, veja falhar (prova que o teste testa algo), escreva o mínimo para passar, refatore. O ganho principal é de design: código testável nasce desacoplado.

Bons testes

  • Nome descreve o comportamento: deposito negativo é rejeitado.
  • Arrange, Act, Assert: preparar, agir, verificar.
  • Prefira fakes a mocks com verificação de chamadas — testam comportamento, não implementação.
  • Determinísticos: injete relógio e aleatoriedade.

No código

✓ ExemploKotlin
class DepositarTest {
  private val metas = FakeMetas(Meta(id = "viagem", valorAlvo = 500_00, guardado = 100_00))
  private val depositar = Depositar(metas)

  @Test fun `deposito soma ao valor guardado`() = runTest {
    val meta = depositar("viagem", 50_00)
    assertEquals(150_00, meta.guardado)
    assertEquals(30, meta.progresso)
  }

  @Test fun `deposito negativo e rejeitado`() = runTest {
    assertFailsWith<IllegalArgumentException> { depositar("viagem", -1) }
  }
}

class FakeMetas(vararg iniciais: Meta) : MetasPort {
  private val dados = iniciais.associateBy { it.id }.toMutableMap()
  override suspend fun buscar(id: String) = dados[id]
  override suspend fun salvar(m: Meta) { dados[m.id] = m }
}

Sem mocks nem banco: o fake de 4 linhas guarda em um Map, e cada teste roda em milissegundos.

Em resumo

Referências

Continue aprendendo

Todos os temas de boas práticas e padrões →