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
Subdeve retornara - 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
- Definimos
Subcomo uma função simples de uma linha; é o alvo do teste. - Declaramos
casoscomo uma slice de struct anônima com camposnome,a,beesperado. - Cada linha da slice representa um cenário: positivos, negativos e zero.
- O
for _, c := range casospercorre a tabela;cé uma cópia do caso atual. t.Run(c.nome, ...)cria um subteste com nome legível, o que ajuda a localizar falhas.- Dentro do subteste comparamos
Sub(c.a, c.b)comc.esperadousandot.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
tpara 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 := cdentro do loop se estiver em versões antigas. - Comparar com
==dentro det.Runsem usart.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