Cómo escribir pruebas unitarias en Go
El autor seleccionó a la Fundación FreeBSD para recibir una donación como parte del programa Write for DOnations .
Introducción
Una prueba unitaria es una función que prueba un fragmento específico de código de un programa o paquete. El trabajo de las pruebas unitarias es verificar la corrección de una aplicación y son una parte crucial del lenguaje de programación Go .
En este tutorial, creará un programa pequeño y luego ejecutará una serie de pruebas en su código utilizando el paquete de Go testing
y el go test
comando . Una vez que complete el tutorial, tendrá un conjunto de pruebas unitarias en funcionamiento que incluye una prueba unitaria basada en tablas , una prueba de cobertura , un punto de referencia y un ejemplo documentado .
Prerrequisitos
Para completar este tutorial, necesitarás lo siguiente:
-
Familiaridad con el lenguaje de programación Go. Visite nuestra serie de tutoriales/libro electrónico, Cómo programar en Go , para obtener una introducción general al lenguaje.
-
Tienes instalada la versión 1.11 o superior de Go en tu equipo local. Puedes seguir estas instrucciones para instalar Go en Linux , macOS y Windows . En macOS, también puedes instalar Go usando el administrador de paquetes Homebrew .
Nota: Este tutorial utiliza módulos Go, que es un sistema de gestión de paquetes introducido en la versión 1.11 de Go. Los módulos Go están pensados para reemplazar $GOPATH y se convirtieron en la opción predeterminada a partir de la versión 1.13 de Go. Para obtener una descripción general más completa de las diferencias entre los módulos Go y $GOPATH, considere leer esta publicación oficial del blog del equipo principal de Go .
Este tutorial fue probado usando la versión 1.14 de Go
Paso 1: Creación de un programa de muestra para realizar pruebas unitarias
Antes de poder escribir pruebas unitarias, necesita algo de código para que sus pruebas lo analicen. En este paso, creará un pequeño programa que sume dos números enteros. En los pasos siguientes, lo utilizará go test
para probar el programa.
Primero, crea un nuevo directorio llamado math
:
- mkdir ./math
Moverse dentro del nuevo directorio:
- cd ./math
Este será el directorio raíz de su programa y ejecutará todos los comandos restantes desde aquí.
Ahora, usando nano
su editor de texto preferido, cree un nuevo archivo llamado math.go
:
- nano math.go
Añade el siguiente código:
./matemáticas/matemáticas.go
package math// Add is our function that sums two integersfunc Add(x, y int) (res int) {return x + y}// Subtract subtracts two integersfunc Subtract(x, y int) (res int) {return x - y}
Aquí estás creando dos funciones llamadas Add
y Subtract
. Cada función acepta dos números enteros y devuelve su suma ( func Add
) o su diferencia ( func Subtract
).
Guarde y cierre el archivo.
En este paso, escribiste código en Go. Ahora, en los siguientes pasos, escribirás algunas pruebas unitarias para asegurarte de que tu código funcione correctamente.
Paso 2: Cómo escribir pruebas unitarias en Go
En este paso, escribirá su primera prueba en Go. Para escribir pruebas en Go, es necesario un vínculo a un archivo de prueba, y este archivo de prueba siempre debe terminar en _test.go
. Por convención, los archivos de prueba de Go siempre se encuentran en la misma carpeta o paquete donde se encuentra el código que están probando. El compilador no crea estos archivos cuando ejecuta el go build
comando, por lo que no debe preocuparse de que terminen en las implementaciones.
Y como ocurre con todo lo que ocurre en Go, el lenguaje tiene opiniones firmes sobre las pruebas. El lenguaje Go ofrece un paquete mínimo pero completo llamadotesting
que los desarrolladores usan junto con el go test
comando. El testing
paquete ofrece algunas convenciones útiles, como pruebas de cobertura y evaluaciones comparativas, que explorará a continuación.
Utilice su editor para crear y abrir un nuevo archivo llamado math_test.go
:
- nano math_test.go
Una función de prueba en Go incluye esta firma: func TestXxxx(t *testing.T)
. Esto significa que todas las funciones de prueba deben comenzar con la palabra Test
, seguida de un sufijo cuya primera palabra esté en mayúscula. Las funciones de prueba en Go reciben solo un parámetro y, en este caso, es un puntero de tipo testing.T
. Este tipo contiene métodos útiles que necesitará para generar resultados, registrar errores en la pantalla y señalar fallas, como el t.Errorf()
método.
Añade el siguiente código a math_test.go
:
./matemáticas/prueba_matemáticas.go
package mathimport "testing"func TestAdd(t *testing.T){ got := Add(4, 6) want := 10 if got != want { t.Errorf("got %q, wanted %q", got, want) }}
Primero, declara el nombre del paquete que desea probar math
. Luego, importa el testing
paquete en sí, lo que hace que estén disponibles el testing.T
tipo y los demás tipos y métodos exportados por el paquete. El código y la lógica de prueba están contenidos en la TestAdd
función.
Resumiendo, las siguientes son características de una prueba en Go:
- El primer y único parámetro debe ser
t *testing.T
- La función de prueba comienza con la palabra
Test
seguida de una palabra o frase que comienza con una letra mayúscula (la convención es utilizar el nombre del método bajo prueba, por ejemplo,TestAdd
) - La prueba llama
t.Error
ot.Fail
para indicar una falla (está llamandot.Error
porque devuelve más detalles quet.Fail
) - Puede utilizarlo
t.Log
para proporcionar información de depuración que no falle - Las pruebas se guardan en archivos utilizando esta convención de nombres:,
foo_test.go
comomath_test.go
.
Guarde y luego cierre el archivo.
En este paso, escribiste tu primera prueba en Go. En el siguiente paso, comenzarás a usarlo go test
para probar tu código.
Paso 3: Probar su código Go usando el go testcomando
En este paso, probará su código. go test
es un subcomando poderoso que lo ayuda a automatizar sus pruebas. go test
acepta diferentes indicadores que pueden configurar las pruebas que desea ejecutar, cuánta verbosidad devuelven las pruebas y más.
Desde el directorio raíz de su proyecto, ejecute su primera prueba:
- go test
Recibirá el siguiente resultado:
OutputPASSok ./math 0.988s
PASS
significa que el código funciona como se esperaba. Cuando una prueba falla, verás FAIL
.
El go test
subcomando solo busca archivos con el _test.go
sufijo . go test
luego escanea esos archivos en busca de funciones especiales, incluidas func TestXxx
y varias otras que cubriremos en pasos posteriores. go test
luego genera un paquete principal temporal que llama a estas funciones de la manera adecuada, las compila y las ejecuta, informa los resultados y finalmente limpia todo.
Probablemente sea go test
suficiente para nuestro pequeño programa, pero habrá ocasiones en las que querrás ver qué pruebas se están ejecutando y cuánto tiempo lleva cada una. Agregar el -v
indicador aumenta la verbosidad. Vuelve a ejecutar tu prueba con el nuevo indicador:
- go test -v
Verá el siguiente resultado:
Output=== RUN TestAdd--- PASS: TestAdd (0.00s)PASSok ./math 1.410s
En este paso, ejecutaste una prueba unitaria básica con el go test
subcomando. En el siguiente paso, escribirás una prueba unitaria más compleja basada en tablas.
Paso 4: Cómo escribir pruebas basadas en tablas en Go
Una prueba basada en tablas es como una prueba unitaria básica, excepto que mantiene una tabla de diferentes valores y resultados. El conjunto de pruebas itera sobre estos valores y los envía al código de prueba. Con este enfoque, podemos probar varias combinaciones de entradas y sus respectivas salidas.
Ahora reemplazará su prueba unitaria con una tabla de estructuras, cuyos campos incluyen los dos argumentos necesarios (dos números enteros) y el resultado esperado (su suma) para su Add
función.
Reabrir math_test.go
:
- nano math_test.go
Elimine todo el código del archivo y agregue en su lugar la siguiente prueba unitaria controlada por tablas:
./matemáticas/prueba_matemáticas.go
package mathimport "testing"// arg1 means argument 1 and arg2 means argument 2, and the expected stands for the 'result we expect'type addTest struct { arg1, arg2, expected int}var addTests = []addTest{ addTest{2, 3, 5}, addTest{4, 8, 12}, addTest{6, 9, 15}, addTest{3, 10, 13}, }func TestAdd(t *testing.T){ for _, test := range addTests{ if output := Add(test.arg1, test.arg2); output != test.expected { t.Errorf("Output %q not equal to expected %q", output, test.expected) } }}
Aquí se define una estructura, se llena una tabla de estructuras que incluyen los argumentos y los resultados esperados para la Add
función y, luego, se escribe una nueva TestAdd
función. En esta nueva función, se itera sobre la tabla, se ejecutan los argumentos, se comparan los resultados con cada resultado esperado y, luego, se devuelven los errores, en caso de que se produzcan.
Guarde y cierre el archivo.
Ahora ejecuta la prueba con la -v
bandera:
- go test -v
Verás el mismo resultado que antes:
Output=== RUN TestAdd--- PASS: TestAdd (0.00s)PASSok ./math 1.712s
Con cada iteración del bucle, el código prueba el valor calculado por la Add
función frente a un valor esperado.
En este paso, escribiste una prueba basada en tablas. En el siguiente paso, escribirás una prueba de cobertura.
Paso 5: Cómo escribir pruebas de cobertura en Go
En este paso, escribirá una prueba de cobertura en Go . Al escribir pruebas, suele ser importante saber qué parte del código real cubren las pruebas. Esto generalmente se conoce como cobertura . Este es también el motivo por el que no ha escrito una prueba para su Subtract
función, para que podamos ver una prueba de cobertura incompleta.
Ejecute el siguiente comando para calcular la cobertura de su prueba unitaria actual:
- go test -coverprofile=coverage.out
Recibirá el siguiente resultado:
OutputPASScoverage: 50.0% of statementsok ./math 2.073s
Go ha guardado estos datos de cobertura en el archivo coverage.out
. Ahora puede presentar los resultados en un navegador web.
Ejecute el siguiente comando:
- go tool cover -html=coverage.out
Se abrirá un navegador web y se mostrarán los resultados:
El texto verde indica cobertura, mientras que el texto rojo indica lo contrario.
En este paso, probaste la cobertura de tu prueba unitaria basada en tablas. En el siguiente paso, realizarás una evaluación comparativa de tu función.
Paso 6: Cómo escribir puntos de referencia en Go
En este paso, escribirá una prueba comparativa en Go . La evaluación comparativa mide el rendimiento de una función o programa. Esto le permite comparar implementaciones y comprender el impacto de los cambios que realiza en su código. Con esa información, puede revelar partes de su código fuente de Go que vale la pena optimizar.
En Go, las funciones que adoptan la forma func BenchmarkXxx(*testing.B)
se consideran puntos de referencia. go test
ejecutará estos puntos de referencia cuando proporcione el -bench
indicador. Los puntos de referencia se ejecutan de forma secuencial.
Agreguemos un punto de referencia a nuestra prueba unitaria.
Abierto math_test.go
:
- nano math_test.go
Ahora agregue una función benchamrk usando la func BenchmarkXxx(*testing.B)
sintaxis:
./prueba_matematica.go
...func BenchmarkAdd(b *testing.B){ for i :=0; i b.N ; i++{ Add(4, 6) }}
La función de referencia debe ejecutar el código de destino bN veces, donde N es un número entero que se puede ajustar. Durante la ejecución de la referencia, bN se ajusta hasta que la función de referencia dure lo suficiente para que se pueda cronometrar de manera confiable. El --bench
indicador acepta sus argumentos en forma de expresión regular.
Guarde y cierre el archivo.
Ahora vamos a usarlo go test
nuevamente para ejecutar nuestro benchmark:
- go test -bench=.
Coincidirán .
con cada función de referencia en un archivo.
También puedes declarar funciones de referencia explícitamente:
- go test -bench=Add
Ejecute cualquiera de los comandos y verá un resultado como este:
Outputgoos: windowsgoarch: amd64pkg: mathBenchmarkAdd-4 1000000000 1.07 ns/opPASSok ./math 2.074s
El resultado significa que el bucle se ejecutó 10.000.000 de veces a una velocidad de 1,07 nanosegundos por bucle.
Nota: Intente no comparar su código Go en un sistema ocupado que se esté utilizando para otros fines, ya que interferirá con el proceso de evaluación comparativa y obtendrá resultados inexactos.
Ahora ha añadido un punto de referencia a su prueba unitaria en crecimiento. En el siguiente y último paso, añadirá ejemplos a su documentación, que go test
también se evaluarán.
Paso 7: documentar el código Go con ejemplos
En este paso, documentarás tu código Go con ejemplos y luego probarás esos ejemplos . Go se centra mucho en la documentación adecuada, y el código de ejemplo agrega otra dimensión tanto a la documentación como a la prueba. Los ejemplos se basan en métodos y funciones existentes. Tus ejemplos deben mostrar a los usuarios cómo usar un fragmento específico de código. Las funciones de ejemplo son el tercer tipo de función tratada especialmente por el go test
subcomando.
Para comenzar, vuelva a abrir math_test.go
,
- nano math_test.go
Ahora, agregue el código resaltado. Esto agregará el fmt
paquete a la lista de importación y su función de ejemplo al final del archivo:
./matemáticas/prueba_matemáticas.go
package mathimport ( "fmt" "testing")// arg1 means argument 1 and arg2 means argument 2, and the expected stands for the 'result we expect'type addTest struct { arg1, arg2, expected int}var addTests = []addTest{ addTest{2, 3, 5}, addTest{4, 8, 12}, addTest{6, 9, 15}, addTest{3, 10, 13},}func TestAdd(t *testing.T) { for _, test := range addTests { if output := Add(test.arg1, test.arg2); output != test.expected { t.Errorf("Output %q not equal to expected %q", output, test.expected) } }}func BenchmarkAdd(b *testing.B) { for i := 0; i b.N; i++ { Add(4, 6) }}func ExampleAdd() { fmt.Println(Add(4, 6)) // Output: 10}
La Output:
línea se utiliza para especificar y documentar el resultado esperado.
Nota : La comparación ignora los espacios iniciales y finales.
Guarde y cierre el archivo.
Ahora vuelva a ejecutar su prueba unitaria:
- go test -v
Verás un resultado actualizado como este:
Output=== RUN TestAdd--- PASS: TestAdd (0.00s)=== RUN ExampleAdd--- PASS: ExampleAdd (0.00s)PASSok ./math 0.442s
Ahora también se prueban sus ejemplos. Esta función mejora su documentación y también hace que sus pruebas unitarias sean más sólidas.
Conclusión
En este tutorial, creó un programa pequeño y luego escribió una prueba unitaria básica para comprobar su funcionalidad. Luego, reescribió su prueba unitaria como una prueba unitaria basada en tablas y luego agregó una prueba de cobertura, un punto de referencia y un ejemplo documentado.
Dedicar tiempo a escribir pruebas unitarias adecuadas es útil para usted como programador porque aumenta su confianza en que el código o programa que ha escrito seguirá funcionando como se espera. El testing
paquete de Go le proporciona considerables capacidades de pruebas unitarias. Para obtener más información, consulte la documentación oficial de Go.
Y si desea aprender más sobre programación en Go, visite nuestra serie de tutoriales / libro electrónico gratuito, Cómo codificar en Go .
Deja una respuesta