Generar código es más fácil. Verificar la intención sigue siendo trabajo.

Las herramientas de IA facilitan producir una implementación que parece razonable. Eso cambia la velocidad con la que proponemos una solución, pero no determina si respeta las reglas del negocio. Una función puede compilar y verse limpia, y aun así contar mal los reintentos o aceptar una configuración inválida.

La documentación de GitHub para Copilot pide revisar y probar las sugerencias generadas. Las pruebas unitarias son una parte práctica de esa revisión: convierten expectativas importantes en comprobaciones ejecutables que permanecen después de la conversación con el asistente.

No todos los cambios asistidos por IA necesitan una suite enorme. El comportamiento importante sí necesita evidencia, sin importar quién escribió la implementación. Cuanto más rápido generamos cambios, más útil resulta un ciclo pequeño de validación en el que podamos confiar.

Definir las expectativas sin depender de la implementación

Un flujo débil consiste en pedir al asistente que implemente una función y después genere pruebas que coincidan con ella. Si ambos pasos comparten una suposición equivocada, podemos terminar con pruebas exitosas alrededor del comportamiento incorrecto. Pasar las pruebas demuestra que se cumplen sus aserciones, no necesariamente los requisitos.

Comienza con ejemplos que una persona pueda explicar sin leer la función. ¿Qué entradas son válidas? ¿Qué sucede en el límite? ¿Qué debe fallar? Entrega esas expectativas al asistente y revisa las aserciones. Cuando el código y las pruebas discrepen, vuelve a los requisitos.

La IA sirve para proponer casos adicionales y escribir código repetitivo de pruebas. Revisar los resultados esperados sigue siendo trabajo de ingeniería. Otro modelo puede aportar una perspectiva, pero no sustituye una regla definida de forma independiente.

Una prueba pequeña en Go puede revelar una gran ambigüedad

Considera una política de reintentos. ¿Un máximo de tres intentos significa tres llamadas en total o tres reintentos después de la llamada inicial? Ambas interpretaciones pueden producir código ordenado. Antes de implementar, define el contrato: maxAttempts incluye la llamada inicial y attempt es el índice, comenzando en cero, de la llamada que acaba de finalizar.

Con ese contrato, los intentos cero y uno permiten otra llamada; el intento dos no. El ejemplo también decide devolver false para intentos negativos o límites no positivos. Es una política explícita de esta función, no una regla universal. Una API pública de configuración podría rechazar esos valores con un error.

Verificar cambios de rendimiento con un benchmark Go reproducible

package retries

import "testing"

// maxAttempts includes the initial call; attempt is zero-based.
func CanRetry(attempt, maxAttempts int) bool {
    return attempt >= 0 && maxAttempts > 0 && attempt < maxAttempts-1
}

func TestCanRetry(t *testing.T) {
    cases := []struct {
        name        string
        attempt     int
        maxAttempts int
        want        bool
    }{
        {"retry after first call", 0, 3, true},
        {"retry after second call", 1, 3, true},
        {"stop after third call", 2, 3, false},
        {"already past the limit", 3, 3, false},
        {"one call means no retries", 0, 1, false},
        {"negative attempt", -1, 3, false},
        {"zero allowed calls", 0, 0, false},
        {"negative limit", 0, -1, false},
    }
    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            if got := CanRetry(tc.attempt, tc.maxAttempts); got != tc.want {
                t.Errorf("CanRetry(%d, %d) = %v; want %v",
                    tc.attempt, tc.maxAttempts, got, tc.want)
            }
        })
    }
}
  • Guarda el ejemplo completo como retry_test.go dentro de un módulo de Go. Ejecuta: go test ./...
  • Cambia temporalmente la comparación final por attempt < maxAttempts. La prueba de la última llamada debe fallar.
  • Restaura la implementación antes de hacer commit. El fallo deliberado comprueba que la prueba detecta el error en el límite.

La cobertura revela huecos; las aserciones definen las comprobaciones

La cobertura puede mostrar qué sentencias se ejecutaron durante una prueba. No puede determinar si el resultado esperado es correcto o si probaste los casos importantes del negocio. Ejecutar una función sin comprobar su resultado puede aumentar la cobertura y aportar poca protección.

Usa la cobertura para orientar la investigación y luego revisa las aserciones. En esta función importa más el límite entre el último reintento permitido y el primero prohibido que agregar varios casos del mismo camino exitoso. Una mutación local deliberada permite comprobar que una aserción detecta el cambio de comportamiento relevante.

Respetar el alcance de las pruebas unitarias

En un servicio en Go o un controlador de Kubernetes, las pruebas unitarias sirven para comprobar validación de configuración, decisiones de políticas y transformaciones de datos. Conviene que sean deterministas y eviten llamadas de red o dependencias del reloj cuando no sean necesarias.

No demuestran que RBAC permita una operación real, que un API server aplique un esquema o que funcione un flujo completo de aprovisionamiento. Esas preguntas requieren pruebas de integración o de extremo a extremo en el límite correspondiente. Simular todas las dependencias puede ocultar una suposición incorrecta sobre el sistema real.

Pruebas de reconciliación en Kubernetes

Usar la IA para acortar el ciclo de validación

Recomiendo definir el comportamiento, pedir una implementación pequeña y revisar tanto el cambio como sus pruebas. Ejecuta las pruebas específicas mientras iteras y las comprobaciones más amplias pertinentes antes de integrar. Un fallo es información que debes investigar, no una razón para cambiar la expectativa hasta que todo pase.

Mantén las pruebas útiles junto al código y ejecútalas en CI. Las futuras refactorizaciones, manuales o asistidas por IA, tendrán que respetar las mismas expectativas documentadas. El objetivo no es acumular archivos de pruebas, sino poder explicar qué importa y demostrar que un cambio lo sigue respetando.

Referencias y lecturas adicionales

¿Necesitas apoyo con tu backend Go?

Colaboro con equipos de ingeniería en proyectos concretos de Go y plataformas Kubernetes, con disponibilidad limitada.

Ver servicios de consultoría Go