Todo funcionando, $GOPATH, HelloWorld,… ahora vamos a hincar el diente de verdad a Go con TDD.
Crearemos una pequeña calculadora con la que aprenderemos a hacer un package, funciones, tocaremos un poco los arrays y además nos servirá para mejorar nuestras skills de TDD.
Empezando por los test
Para empezar con la calculadora, vamos a crear un package llamado calculator, así que creamos un directorio con ese mismo nombre (calculator) y nuestro primer fichero será calculator_test.go
(TDD a tope).
*nota: solo puede existir un package por directorio
El código de ese primer test será algo así:
package calculator
import "testing"
func TestAdder(t *testing.T) {
sum := Add(2, 2)
expected := 4
if sum != expected {
t.Errorf("expected '%d' but got '%d'", expected, sum)
}
}
Si nos hemos fijado un poco en el mensaje de error, vemos que ahora utilizamos %d en lugar de %s porque lo que queremos es imprimir por consola un número.
Si ejecutamos go test
nos aparecerá que no esta definida la función Add
$ go test # github.com/jeslopcru/golang-examples/02-calculator ./calculator_test.go:8:9: undefined: Add Compilation finished with exit code 2

Mínimo código para que funcione
Ahora lo minimo que tenemos que hacer para que pase el test es crear el fichero calculator.go
y la función Add, algo así:
package calculator
func Add(x int, y int) int {
return 4;
}
Si ejecutamos esto, tendremos nuestro test en verde. Siendo puristas, vamos por el camino correcto, ahora añadiríamos otro caso y lo haríamos pasar para obtener la función.
$ go test === RUN TestAdder --- PASS: TestAdder (0.00s) PASS
No vamos a extendernos mucho con esto. Al el test quedaría algo así:
func TestAdder(t *testing.T) {
sum := Add(2, 2)
expected := 4
if sum != expected {
t.Errorf("expected '%d' but got '%d'", expected, sum)
}
otherSum := Add(3, 2)
otherExpected := 5
if otherSum != otherExpected {
t.Errorf("expected '%d' but got '%d'", otherExpected, otherSum)
}
}
y el código de producción sería:
func Add(x int, y int) int {
return x + y
}
el refactor lo es todo
Ahora que ya tenemos los tests en verde con el código de producción listo, nos queda el siguiente paso: Refactor
Parece que aquí tenemos poco código que tocar, pero todo no es el código también la documentación.
Bola extra: Leyendo la documentación de los paquetes
Para ver la documentación de los paquetes que tenemos descargados, abrimos un terminal y escribimos:
$ godoc -http=:6060
Ahora entrando en [localhost:6060/pkg/] (localhost:6060/pkg/) veremos toda la lista de paquetes descargados y podemos buscar el nuestro: http://localhost:6060/pkg/github.com/jeslopcru/golang-examples/02-calculator/
* Esta pagina corresponde al proyecto https://github.com/jeslopcru/golang-examples
Como vemos esa página está desierta, así que vamos a añadir un poco de documentación.
Lo primero será añadir documentación a la función. Con solo poner un comentario ya se añadirá aquí.
// GIVEN two integers WHEN call Add function THEN result is sum of them
func Add(x int, y int) int {
return x + y
}
Si además queremos añadir un ejemplo, lo único que tenemos que hacer es añadir un test como este:
func ExampleAdd() {
sum := Add(2, 4)
fmt.Println(sum)
// Output: 6
}
Notemos que tenemos un comentario con la salida esperada, si ejecutamos los tests tendremos algo como esto:
╰─$ go test -v
=== RUN TestAdder
--- PASS: TestAdder (0.00s)
=== RUN ExampleAdd
--- PASS: ExampleAdd (0.00s)
PASS
Si eliminamos el comentario // Output: 6
el segundo test no se ejecutará.
Añadiremos comentarios antes de las funciones para describir su funcionamiento. Si además queremos enriquecer esa documentación podemos hacer ejemplos como ExampleAdd.
Eso hará que la página de documentación sea más amigable y encima si publicamos la librería haremos el camino más fácil para poder utilizarla. De momento nosotros con saber que existen los test de tipo example nos vale.
Nuevo caso: Sumando varios números
Vamos a seguir aprendiendo Go y ahora es la hora de los arrays. Para ello vamos a implementar una nueva funcionalidad de nuestra calculadora.
Vamos a sumar todos los números de un array. En principio, empezaremos con un array de 5 elementos, por simplificar.
Test de suma multiple
Como somos fieles seguidores de TDD, lo primero será crear un nuevo test como este:
func TestAddMultiple(t *testing.T) {
numbers := [5]int{1, 2, 3, 4, 5}
got := AddMultiple(numbers)
expected := 15
if expected != got {
t.Errorf("got %d expected %d given, %v", got, expected, numbers)
}
}
Así que al ejecutarlo tenemos:
$ go test
# github.com/jeslopcru/golang-examples/02-calculator
./calculator_test.go:27:9: undefined: AddMultiple
Ahora vamos a aprender un poco sobre los array en Go. En Go los arrays se declaran e inicializan de 2 maneras: con longitud fija,
//con una longitud fija
numbers := [3]int{1, 2, 3}
//sin declarar la longitud
numbers := [...]int{1, 2, 3, 4, 5}
Otra de las cosas nuevas que estamos aprendiendo con este test, es la manera de imprimir por pantalla arrays. Para hacerlo utilizamos %v que es el comportamiento por defecto y nos sirve perfectamente para imprimir arrays.
Implementando la suma múltiple
Haciendo el mínimo código para que pase.
//GIVEN an array WHEN call AddMultiple function THEN result is sum of elements
func AddMultiple(numberList [5]int) int {
result := 0
for i := 0; i < 5; i++ {
result = Add(result, numberList[i])
}
return result
}
Ejecutamos los test y como vemos que todo está en verde, podemos hacer un commit.
$ go test === RUN TestAdder --- PASS: TestAdder (0.00s) === RUN TestAddMultiple --- PASS: TestAddMultiple (0.00s) === RUN ExampleAdd --- PASS: ExampleAdd (0.00s) PASS
Refactorizando y aprendiendo range
Vamos a intentar usar toda la potencia de Go utilizando range
//GIVEN an array WHEN call AddMultiple function THEN result is sum of elements
func AddMultiple(numberList [5]int) int {
result := 0
for _, number := range numberList {
result = Add(result, number)
}
return result
}
Con range podemos iterar a través del array de una manera mucho más limpia.
además con «_» estamos ignorando el indice del array.
Bueno, comprobamos que seguimos estando en verde y así ya podemos hacer otro commit con un ammend para dejar el código perfecto.
Sumando array multiples
Hemos empezado con una funcionalidad muy sencilla de un array de 5 elementos. Ahora vamos a extrapolar la solución a un array de n elementos.
Para ello vamos a añadir un nuevo case a nuestro test así:
func TestAddMultiple(t *testing.T) {
t.Run("array of 5 numbers", func(t *testing.T) {
numbers := [5]int{1, 2, 3, 4, 5}
got := AddMultiple(numbers)
expected := 15
if expected != got {
t.Errorf("got %d expected %d given, %v", got, expected, numbers)
}
})
t.Run("array of any syze", func(t *testing.T) {
numbers := []int{1, 2}
got := AddMultiple(numbers)
expected := 3
if expected != got {
t.Errorf("got %d expected %d given, %v", got, expected, numbers)
}
})
}
En un primer momento, GoLand nos indica un error de type int, porque espera un array de 5 elementos.
Si cambiamos la firma del método AddMultiple lo que nos pasará será que el otro test falla, así que lo podemos solucionar de 2 maneras:
- Cambiando la API, es decir, cambiamos la firma para que acepte arrays de cualquier numero de elementos y por tanto cambiamos el test que falla (el de fijo a 5).
- Creando un nuevo método.
Vamos a optar por la primera opción. Así que el código quedaría así:
//GIVEN an array WHEN call AddMultiple function THEN result is sum of elements
func AddMultiple(numberList []int) int {
result := 0
for _, number := range numberList {
result = Add(result, number)
}
return result
}
Y en los test solo tenemos que cambiar la declaración de los arrays para que no sean fijos (numbers := []int{1, 2, 3, 4, 5}
)
Bola Extra: cobertura de test
Lo importante no es tener muchos tests, cuantos más test más difícil de mantener será la suite.
Debemos mantener un equilibrio y fijarnos sobre todo en la calidad de los mismos.
En nuestro caso, mantener las pruebas duplicadas es redundante. En un principio estas pruebas nos aportaron valor, pero ahora que tenemos el código funcionando… estas pruebas son redundantes.
Del mismo modo, es importante saber que parte del código no está cubierto de test, para ello tenemos https://blog.golang.org/cover
Para utilizarlo:
╰─$ go test -cover
PASS
coverage: 100.0% of statements
ok github.com/jeslopcru/golang-examples/02-calculator 0.007s
Si eliminamos las pruebas redundantes seguiremos obteniendo el 100%. Aunque el objetivo nunca debe ser conseguir el 100% de cobertura.
El objetivo es tener unas pruebas robustas y confiables. Siendo estrictos con TDD, llegaremos al 100% de cobertura fácilmente y sin esfuerzo, otra cuestión es si esa cobertura nos proporciona la confianza suficiente como para modificar el código.
Conclusiones
Seguimos aprendiendo Go. Ahora ya sabemos crear un package, funciones que hacen cálculos matemáticos. Todo tiene tests y además hemos aprendido a añadir documentación con ejemplos.
Nuestro flujo de TDD sigue su curso. Hemos aprendido como trabajar con arrays, hacer bucles con y cobertura de test.
Y la lección más importante que no debemos olvidar es que el objetivo es tener unas pruebas robustas y confiables. Siendo estrictos con TDD, llegaremos al 100% de cobertura fácilmente y sin esfuerzo.
Y ahora, ¿seremos capaces de añadir una función para restar?
2 comentarios en “Poco a poco con Go y TDD: package, funciones, bucles, arrays y cobertura de test”