Tan importante como programar nuevas funcionalidades es asegurarse de que no se rompe nada en cada cambio, afortunadamente Go lleva incluÃdo todo lo necesario para ejecutar tests de forma rápida y cómoda.
Si eres nuevo en el mundo de Go te recomiendo los siguientes artÃculos anteriores:
Para conseguir una construcción reproducible Go recomienda utilizar versiones similares de Go, para ello podemos generar el fichero go.mod de forma manual o inicializar un módulo y lo generará Go por nosotros:
cd go_testing/
go mod init name
Podemos ver el contenido del fichero go.mod:
module name
go 1.20
Construcción Reproducible:
La inclusión de un archivo go.mod en tu proyecto puede contribuir a la construcción reproducible. Esto significa que, al compartir tu código con otros desarrolladores o al ejecutar pruebas en diferentes entornos, las mismas versiones de las dependencias se utilizarán, lo que puede ayudar a evitar problemas de compatibilidad.
A modo de ejemplo simplemente tendremos una función llamada Hello que si le llega un string de entrada, devolverá [string,nil]
, si se le pasa una entrada vacÃa devolverá ["",ERROR]
package main
import (
"errors"
"fmt"
)
func Hello(name string) (string, error) {
if name == "" {
return name, errors.New("Empty name")
}
return name, nil
}
func main() {
name, error := Hello("Kr0m")
if error != nil {
fmt.Println("ERROR:", error)
} else {
fmt.Println("Name:", name)
}
}
Creamos el test unitario que comprobará que llamando a la función Hello() esta devuelva el valor correcto:
package main
import (
"testing"
"regexp"
)
// Test Hello() function with input string:
func TestHelloName(t *testing.T) {
name := "Kr0m" // Function input
want := regexp.MustCompile(`\b`+name+`\b`) // What we expect
msg, err := Hello(name) // Save Hello() returned values
// Compare want with msg OR an error has ocurred:
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello(name) = %q, %v, want match for %#q, nil`, msg, err, want)
}
}
// Test Hello() function with empty string:
func TestHelloEmpty(t *testing.T) {
msg, err := Hello("") // Save Hello() returned values
// If msg is not empty string OR err NULL:
if msg != "" || err == nil {
t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
}
}
Pasamos los tests sin problemas:
PASS
ok name 0.002s
Introducimos un bug, haciendo que la función siempre retorne [AAAA,nil]
:
package main
import (
"errors"
"fmt"
)
func Hello(name string) (string, error) {
if name == "" {
return name, errors.New("Empty name")
}
name = "AAAA" // BUG
return name, nil
}
func main() {
name, error := Hello("Kr0m")
if error != nil {
fmt.Println("ERROR:", error)
} else {
fmt.Println("Name:", name)
}
}
Volvemos a pasar los tests:
Podemos ver que esta vez ha fallado:
--- FAIL: TestHelloName (0.00s)
name_test.go:15: Hello(name) = "AAAA", <nil>, want match for `\bKr0m\b`, nil
FAIL
exit status 1
FAIL name 0.002s
La lÃnea 15 de name_test:
if !want.MatchString(msg) || err != nil {
t.Fatalf(`Hello(name) = %q, %v, want match for %#q, nil`, msg, err, want)
}
Efectivamente want
no machea msg
:
name := "Kr0m"
want := regexp.MustCompile(`\b`+name+`\b`)
// msg content: AAAA