tests-unitarios-go-testing

Tests unitarios en Go con testing y convenciones clave

  • 3 min

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
}
Copied!

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)
    }
}
Copied!

Hay tres convenciones importantes:

  1. El fichero termina en _test.go.
  2. La función empieza por Test.
  3. La función recibe t *testing.T.

Ejecutar los tests

Desde la carpeta del módulo:

go test
Copied!

Si todo va bien, veremos algo parecido a:

PASS
ok      ejemplo/calculadora    0.003s
Copied!

Para ejecutar todos los paquetes del proyecto:

go test ./...
Copied!

Ese ./... significa: este paquete y todos los subpaquetes.

Fallar un test

Si cambiamos el esperado:

esperado := 6
Copied!

El test fallará:

--- FAIL: TestSumar (0.00s)
    calculadora_test.go:10: Sumar(2, 3) = 5; esperado 6
FAIL
Copied!

El 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)
    }
}
Copied!

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 calculadora
Copied!

O en un paquete externo:

package calculadora_test
Copied!

La 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 ./...
Copied!

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.