Paquete testing en Go
El paquete testing es esencial para crear pruebas robustas en Go. Esta guía ampliada cubre desde fundamentos hasta técnicas avanzadas, incluyendo mejores prácticas y ejemplos del mundo real.
Componentes Principales
1. Pruebas Unitarias (testing.T)
Métodos Clave:
Error()/Errorf(): Reportar fallos sin detener la prueba.Fatal()/Fatalf(): Detener la prueba inmediatamente.Run(): Ejecutar subtests.Parallel(): Ejecutar pruebas en paralelo.Helper(): Marcar función como helper.
Ejemplo de Tabla de Pruebas:
func TestSuma(t *testing.T) {
casos := []struct {
a, b, esperado int
}{
{2, 3, 5},
{0, 0, 0},
{-1, 1, 0},
{100, 200, 300},
}
for _, c := range casos {
t.Run(fmt.Sprintf("%d+%d", c.a, c.b), func(t *testing.T) {
resultado := Suma(c.a, c.b)
if resultado != c.esperado {
t.Errorf("Esperado %d, obtenido %d", c.esperado, resultado)
}
})
}
}
Mejores Prácticas:
- Usar subtests para organizar casos.
- Proporcionar nombres descriptivos en
t.Run().
2. Benchmarks (testing.B)
Métodos Clave:
ResetTimer(): Ignorar tiempo de inicialización.ReportAllocs(): Monitorear asignaciones de memoria.
Ejemplo Comparativo:
func BenchmarkSuma(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
Suma(10, 20)
}
}
func BenchmarkSumaOptimizada(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
SumaOptimizada(10, 20)
}
}
Interpretación de Resultados:
go test -bench . -benchmem
# Output:
# BenchmarkSuma-8 1000000000 0.250 ns/op 0 B/op 0 allocs/op
3. Ejemplos (Example)
Convenciones:
- Nombre de función:
ExampleFuncion(). - Salida esperada:
// Output: resultado.
Ejemplo con Documentación:
func ExampleSuma() {
fmt.Println(Suma(3, 4))
// Output: 7
}
4. Configuración Global (testing.M)
Uso para Setup/Teardown:
func TestMain(m *testing.M) {
fmt.Println("Inicializando base de datos...")
db = setupDB()
codigo := m.Run()
fmt.Println("Limpiando recursos...")
db.Close()
os.Exit(codigo)
}
Funcionalidades Avanzadas
1. Pruebas en Paralelo
func TestDescargas(t *testing.T) {
t.Parallel()
// Pruebas de descargas simultáneas
}
func TestSubida(t *testing.T) {
t.Parallel()
// Pruebas de subida
}
2. Coverage y Perfilado
Generar Reporte de Cobertura:
go test -coverprofile=coverage.out
go tool cover -html=coverage.out
Perfilado de CPU:
go test -cpuprofile=cpu.out
go tool pprof cpu.out
Mejores Prácticas
1. Diseño de Pruebas
-
Pruebas Deterministas: Evitar dependencias de estado global.
- Aislamiento: Cada test debe ser independiente.
- Nombres Descriptivos:
TestParsearFecha_FormatoInvalido.
2. Manejo de Errores
func TestLeerArchivo(t *testing.T) {
data, err := os.ReadFile("testdata/input.txt")
if err != nil {
t.Fatalf("No se pudo leer archivo: %v", err)
}
// ...
}
3. Mocking con Interfaces
type DB interface {
ObtenerUsuario(id int) (*Usuario, error)
}
func TestServicio(t *testing.T) {
mockDB := &MockDB{
usuarios: map[int]*Usuario{1: {"Admin"}},
}
servicio := NuevoServicio(mockDB)
// ...
}
Errores Comunes
1. Dependencia de Orden
// ❌ Incorrecto: Tests dependen de ejecución previa
var contador int
func TestA(t *testing.T) { contador++ }
func TestB(t *testing.T) { contador++ }
2. Ignorar Errores
// ❌ Incorrecto
resultado, _ := FuncionRiesgosa()
// ✅ Correcto
resultado, err := FuncionRiesgosa()
if err != nil {
t.Fatal(err)
}
3. Pruebas Lentas
func TestAPI(t *testing.T) {
if testing.Short() {
t.Skip("Skip en modo corto")
}
// Llamada HTTP real
}
Integración con Herramientas Externas
1. Testify para Aserciones
import (
"github.com/stretchr/testify/assert"
)
func TestDivision(t *testing.T) {
resultado, err := Dividir(10, 2)
assert.NoError(t, err)
assert.Equal(t, 5, resultado)
}
2. httptest para APIs HTTP
func TestHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/", nil)
w := httptest.NewRecorder()
MiHandler(w, req)
assert.Equal(t, http.StatusOK, w.Code)
}
Comandos Útiles
| Comando | Descripción |
|---|---|
go test -v |
Modo verbose |
go test -run TestNombre |
Ejecutar prueba específica |
go test -race |
Detectar race conditions |
go test -shuffle=on |
Ejecutar tests en orden aleatorio |
Conclusión
El paquete testing en Go ofrece un conjunto poderoso de herramientas para garantizar la calidad del código. Al dominar sus características y seguir buenas prácticas, podrás construir sistemas confiables y mantenibles. Combina pruebas unitarias, benchmarks y ejemplos para cubrir todos los aspectos de tu aplicación.