Un test unitario en Go es una función que comprueba una pieza pequeña del código de forma automática.
Go incluye soporte de pruebas en la biblioteca estándar. No hace falta instalar un framework para empezar, aunque podemos añadir utilidades externas si el proyecto las necesita.
Vamos a ver la forma idiomática, empezando por una función sencilla.
Código a probar
Supongamos que tenemos este fichero calculadora.go:
package calculadora
func Sumar(a, b int) int {
return a + b
}Queremos comprobar que Sumar hace lo que dice. El ejemplo es pequeño, pero nos permite centrarnos en las convenciones del paquete testing.
Crear el fichero de test
Los tests viven en ficheros que terminan en _test.go.
Creamos calculadora_test.go:
package calculadora
import "testing"
func TestSumar(t *testing.T) {
resultado := Sumar(2, 3)
esperado := 5
if resultado != esperado {
t.Errorf("Sumar(2, 3) = %d; esperado %d", resultado, esperado)
}
}Hay tres convenciones importantes:
- El fichero termina en
_test.go. - La función empieza por
Test. - La función recibe
t *testing.T.
Ejecutar los tests
Desde la carpeta del módulo:
go testSi todo va bien, veremos algo parecido a:
PASS
ok ejemplo/calculadora 0.003sPara ejecutar todos los paquetes del proyecto:
go test ./...Ese ./... significa: este paquete y todos los subpaquetes.
Fallar un test
Si cambiamos el esperado:
esperado := 6El test fallará:
--- FAIL: TestSumar (0.00s)
calculadora_test.go:10: Sumar(2, 3) = 5; esperado 6
FAILEl mensaje debe explicar qué ha pasado. Un test que falla con un mensaje claro es mucho más útil que uno que solo grita “mal”.
t.Fatal y t.Error
t.Error marca el test como fallido, pero sigue ejecutando la función.
t.Fatal marca el test como fallido y detiene ese test inmediatamente.
func TestDividir(t *testing.T) {
resultado, err := Dividir(10, 2)
if err != nil {
t.Fatalf("no se esperaba error: %v", err)
}
if resultado != 5 {
t.Errorf("resultado = %d; esperado 5", resultado)
}
}Usamos Fatal cuando seguir no tiene sentido. Por ejemplo, si no tenemos resultado válido porque la función devolvió error.
Tests en el mismo paquete o en uno externo
Podemos escribir tests en el mismo paquete:
package calculadoraO en un paquete externo:
package calculadora_testLa segunda opción obliga a probar el paquete como lo usaría alguien desde fuera, solo con su API exportada. Es más estricta y suele venir bien para tests de comportamiento. Los tests del mismo paquete, en cambio, pueden acceder a detalles no exportados.
Cobertura y detector de carreras
Podemos combinar la ejecución completa con cobertura y detección de carreras:
go test -cover ./...
go test -race ./...La cobertura indica qué código ejecutaron las pruebas, no si las comprobaciones son buenas. El detector de carreras solo encuentra conflictos que ocurren en las rutas ejecutadas durante la prueba.