Pruebas en Go: unit, integration, e2e a nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué las pruebas avanzadas son un must have en Go



Go (Golang) es conocido por su simplicidad y sus herramientas integradas para pruebas. Sin embargo, cuando un proyecto crece más allá del "Hello, World", el estándar go test deja de ser suficiente. A nivel avanzado, los desarrolladores se enfrentan a preguntas como: cómo aislar correctamente las pruebas, cómo probar el trabajo con bases de datos, cómo simular servicios externos y cómo organizar una verificación integral de toda la aplicación.



En este artículo, analizaremos tres niveles de pruebas — Unit, Integration y E2E — con ejemplos de la práctica real. Aprenderá a usar mocks, contenedores para pruebas de integración y a construir un pipeline fiable para CI/CD. El material está dirigido a desarrolladores que ya conocen los fundamentos de Go y desean llevar la calidad de su código al siguiente nivel.



1. Pruebas unitarias: aislamiento, mocks y el patrón Table-Driven Tests



Las pruebas unitarias verifican el funcionamiento de un solo módulo (función, método) en completo aislamiento de las dependencias externas. En Go, para esto se suelen usar interfaces y bibliotecas de mocks, como testify/mock o gomock.



1.1. Interfaces como base de las pruebas



Para reemplazar un servicio real por un mock, el código debe depender de una interfaz, no de una implementación concreta. Veamos un ejemplo de un servicio para enviar notificaciones.



// notifier.gotype Notifier interface {    Send(to string, message string) error}

type UserService struct { Notifier Notifier}

func (s *UserService) NotifyUser(userID string) error { // lógica para obtener el email del usuario email := userID + "@example.com" return s.Notifier.Send(email, "Welcome!")}


1.2. Creación de un mock con testify/mock



Instalamos la biblioteca: go get github.com/stretchr/testify.



// notifier_mock.go (en el paquete user_test)type NotifierMock struct {    mock.Mock}

func (m *NotifierMock) Send(to string, message string) error { args := m.Called(to, message) return args.Error(0)}


1.3. Table-Driven Tests



Este es un patrón estándar en Go para probar múltiples casos. Mejora la legibilidad y simplifica la adición de nuevas pruebas.



// user_service_test.gofunc TestNotifyUser(t *testing.T) {    tests := []struct {        name      string        userID    string        mockError error        expectErr bool    }{        {"success", "user1", nil, false},        {"fail_send", "user2", errors.New("send error"), true},    }

for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockNotifier := new(NotifierMock) service := &UserService{Notifier: mockNotifier}

mockNotifier.On("Send", tt.userID+"@example.com", "Welcome!").Return(tt.mockError)

err := service.NotifyUser(tt.userID) if tt.expectErr { assert.Error(t, err) } else { assert.NoError(t, err) } mockNotifier.AssertExpectations(t) }) }}


Best practice: Use t.Run para subpruebas — esto permite ejecutar cada caso por separado y ver cuál falló exactamente.



2. Pruebas de integración: trabajo con BD y servicios externos



Las pruebas de integración verifican la interacción de varios componentes: código Go con la base de datos, servidor API con la caché, etc. El principal desafío es gestionar el estado del entorno. Aquí es donde entran en juego los Testcontainers.



2.1. Testcontainers para Go



Testcontainers permiten levantar contenedores Docker directamente desde las pruebas. Esto proporciona un entorno limpio y aislado para cada ejecución.



Instalación: go get github.com/testcontainers/testcontainers-go



2.2. Ejemplo: pruebas

Blogs

Book Recommendations