Testes básicos
Os testes permitem verificar se as funções de um programa estão funcionando como esperado, comparando os resultados obtidos com os valores que deveriam ser retornados. Para isso temos o package testing, que faz parte da biblioteca padrão e permite criar testes automatizados sem precisar instalar bibliotecas externas.
Isso ajuda a identificar erros durante o desenvolvimento, principalmente quando modificamos alguma parte do código e precisamos garantir que as funcionalidades existentes continuem funcionando.
Criando testes com testing
Os testes são escritos em arquivos que terminam com _test.go. Esses arquivos normalmente ficam no mesmo diretório do código que queremos testar. Por exemplo, imagine um projeto com um package chamado calculadora.
projeto/
│
├── go.mod
│
└── calculadora/
├── calculadora.go
└── calculadora_test.go
No arquivo calculadora.go, temos uma função responsável por somar dois números.
package calculadora
// Somar recebe dois números e retorna a soma.
func Somar(a, b int) int {
return a + b
}
Agora podemos criar um teste para verificar se essa função retorna o resultado esperado.
// calculadora_test.go
package calculadora
import "testing"
func TestSomar(t *testing.T) {
// Executa a função que queremos testar.
resultado := Somar(10, 20)
// Verifica se o resultado está correto.
if resultado != 30 {
t.Errorf("Esperado 30, mas recebeu %d", resultado)
}
}
Para que o Go reconheça uma função como teste, seu nome deve começar com Test, seguido de um nome que comece com letra maiúscula, e ainda precisa receber um parâmetro do tipo *testing.T. Esse testing.T representa o teste em execução e oferece métodos para registrar erros e controlar seu resultado.
t.Errorf("Esperado 30, mas recebeu %d", resultado)
No código acima, estamos informando que o teste falhou, porque o resultado recebido foi diferente do esperado. O Errorf registra a falha, mas permite que o restante do teste continue executando. Se o resultado estiver correto, o teste termina sem registrar erros.
Executando testes com go test
Para executar os testes, usamos o comando go test, que procura os arquivos terminados em _test.go, compila o código necessário e executa as funções de teste. Dentro do diretório calculadora, podemos executar:
go test
Resultado possível:
PASS
ok exemplo.com/projeto/calculadora 0.003s
Se algum teste falhar, o Go apresentará informações sobre a falha, permitindo identificar qual resultado foi diferente do esperado. Também podemos executar todos os testes dos packages do nosso módulo, a partir da raiz do projeto.
go test ./...
Para mostrar informações mais detalhadas sobre cada teste, usamos -v.
go test -v ./...
Resultado possível:
=== RUN TestSomar
--- PASS: TestSomar (0.00s)
PASS
ok exemplo.com/projeto/calculadora 0.003s
Nesse exemplo, PASS indica que o teste passou. Quando uma verificação falha, o Go apresenta FAIL e as mensagens registradas durante o teste. O go test também permite executar testes específicos, usando a opção -run.
go test -run '^TestSomar$'
Isso executa apenas o teste cujo nome corresponde a TestSomar, dentro do package atual.
Testes de tabela
Os testes de tabela (table-driven tests) são uma prática comum em Go, que permite testar uma mesma função com vários valores diferentes, sem precisar criar uma função de teste para cada situação. Para isso, podemos criar um slice de structs contendo os valores de entrada e os resultados esperados. Depois, percorremos esses valores e executamos o mesmo teste para cada caso.
Por exemplo, podemos substituir o teste anterior por:
package calculadora
import "testing"
func TestSomar(t *testing.T) {
// Define os casos que serão testados.
testes := []struct {
nome string
a int
b int
esperado int
}{
{"Números positivos", 10, 20, 30},
{"Números negativos", -10, -20, -30},
{"Soma com zero", 10, 0, 10},
}
// Percorre os casos de teste.
for _, teste := range testes {
t.Run(teste.nome, func(t *testing.T) {
resultado := Somar(teste.a, teste.b)
// Compara o resultado com o valor esperado.
if resultado != teste.esperado {
t.Errorf(
"Esperado %d, mas recebeu %d",
teste.esperado,
resultado,
)
}
})
}
}
Nesse exemplo, criamos três casos de teste, cada um contendo os números que serão enviados para a função e o resultado esperado. Depois, usamos for range para percorrer os casos e t.Run() para executar cada um como um subteste, permitindo identificar separadamente quais passaram ou falharam.
Ao executar:
go test -v
Teremos um resultado parecido com:
=== RUN TestSomar
=== RUN TestSomar/Números_positivos
=== RUN TestSomar/Números_negativos
=== RUN TestSomar/Soma_com_zero
--- PASS: TestSomar (0.00s)
--- PASS: TestSomar/Números_positivos (0.00s)
--- PASS: TestSomar/Números_negativos (0.00s)
--- PASS: TestSomar/Soma_com_zero (0.00s)
PASS
ok exemplo.com/projeto/calculadora 0.003s
Observe que o Go executou os três casos de teste e mostrou o resultado de cada um separadamente. Se modificarmos a função Somar() e algum resultado deixar de ser o esperado, o teste correspondente falhará, ajudando a identificar o problema. Os testes de tabela são bastante usados em Go porque permitem verificar diferentes comportamentos de uma função mantendo o código organizado, principalmente quando precisamos testar valores válidos, inválidos e situações específicas.