Fácil Go

Teste de subtração com table-driven

Enunciado

Crie uma função Sub(a, b int) int que retorna a - b e um teste table-driven que verifique pelo menos três casos, incluindo um resultado negativo.

Requisitos

  • A função Sub deve retornar a - b.
  • O teste deve usar uma slice de structs com campos nome, a, b, esperado.
  • Cada caso deve rodar dentro de t.Run(c.nome, ...).
  • Deve haver um caso cujo resultado seja negativo.

Código inicial

package calculadora

import "testing"

func Sub(a, b int) int {
	// implemente
	return 0
}

func TestSub(t *testing.T) {
	// implemente o table-driven test
}

Saída esperada

=== RUN   TestSub
=== RUN   TestSub/positivos
=== RUN   TestSub/negativos
=== RUN   TestSub/zero
--- PASS: TestSub (0.00s)
    --- PASS: TestSub/positivos (0.00s)
    --- PASS: TestSub/negativos (0.00s)
    --- PASS: TestSub/zero (0.00s)
PASS
ok  	calculadora	0.002s
Ver dica

Comece declarando a slice de casos com uma struct anônima; depois itere com for _, c := range casos e chame t.Run.

Mostrar solução
package calculadora

import "testing"

// Sub retorna a diferença entre a e b.
func Sub(a, b int) int {
	return a - b
}

func TestSub(t *testing.T) {
	// Cada caso é uma linha da tabela: nome + entradas + saída esperada.
	casos := []struct {
		nome     string
		a, b     int
		esperado int
	}{
		{"positivos", 10, 3, 7},
		{"negativos", 3, 10, -7},
		{"zero", 5, 5, 0},
	}

	// Um único loop roda todos os casos como subtestes nomeados.
	for _, c := range casos {
		t.Run(c.nome, func(t *testing.T) {
			obtido := Sub(c.a, c.b)
			if obtido != c.esperado {
				t.Errorf("Sub(%d, %d) = %d; esperado %d", c.a, c.b, obtido, c.esperado)
			}
		})
	}
}

Passo a passo

  1. Definimos Sub como uma função simples de uma linha; é o alvo do teste.
  2. Declaramos casos como uma slice de struct anônima com campos nome, a, b e esperado.
  3. Cada linha da slice representa um cenário: positivos, negativos e zero.
  4. O for _, c := range casos percorre a tabela; c é uma cópia do caso atual.
  5. t.Run(c.nome, ...) cria um subteste com nome legível, o que ajuda a localizar falhas.
  6. Dentro do subteste comparamos Sub(c.a, c.b) com c.esperado usando t.Errorf, que marca falha mas continua.

Por que funciona

O padrão table-driven separa os dados de teste da lógica de verificação. Adicionar um caso novo é só acrescentar uma linha na slice; o loop e o t.Run cuidam do resto. Isso reduz duplicação e mantém o teste legível.

t.Run também isola cada subteste: se um falhar, os outros continuam executando, e a saída do go test -v mostra exatamente qual nome falhou.

Erros comuns

  • Esquecer de passar t para a closure: t.Run(c.nome, func() { ... }) não compila, pois a assinatura esperada é func(*testing.T).
  • Capturar a variável de loop errada (antes do Go 1.22 isso causava bugs): use c := c dentro do loop se estiver em versões antigas.
  • Comparar com == dentro de t.Run sem usar t.Errorf/t.Fatalf — o teste passa mesmo com resultado errado.

Outra forma de resolver

Sem table-driven, escrevendo um teste por caso:

func TestSubPositivos(t *testing.T) {
	if Sub(10, 3) != 7 {
		t.Fatal("esperado 7")
	}
}

É preferível quando há pouquíssimos casos e eles têm lógica muito diferente entre si.

Saída esperada

=== RUN   TestSub
=== RUN   TestSub/positivos
=== RUN   TestSub/negativos
=== RUN   TestSub/zero
--- PASS: TestSub (0.00s)
    --- PASS: TestSub/positivos (0.00s)
    --- PASS: TestSub/negativos (0.00s)
    --- PASS: TestSub/zero (0.00s)
PASS
ok  	calculadora	0.002s