Nombre de paquete adecuado para realizar pruebas con el idioma Go

102

He visto varias estrategias de nomenclatura de paquetes de prueba diferentes dentro de Go y quería saber cuáles son las ventajas y desventajas de cada una y cuál debería usar.

Estrategia 1:

Nombre de archivo: github.com/user/myfunc.go

package myfunc

Nombre del archivo de prueba: github.com/user/myfunc_test.go

package myfunc

Consulte bzip2 para ver un ejemplo.

Estrategia 2:

Nombre de archivo: github.com/user/myfunc.go

package myfunc

Nombre del archivo de prueba: github.com/user/myfunc_test.go

package myfunc_test

import (
    "github.com/user/myfunc"
)

Ver cable para un ejemplo.

Estrategia 3:

Nombre de archivo: github.com/user/myfunc.go

package myfunc

Nombre del archivo de prueba: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

Vea cadenas para un ejemplo.

La biblioteca estándar de Go parece utilizar una combinación de la estrategia 1 y 2. ¿Cuál de las tres debería utilizar? Es una molestia agregar package *_testmis paquetes de prueba, ya que significa que no puedo probar los métodos privados de mi paquete, pero tal vez haya una ventaja oculta que no conozco.

Dan
fuente
9
Esta pregunta solo dará lugar a opiniones variadas, pero arrojaré la mía. No debería necesitar probar sus métodos privados. Quiere probar la interfaz de su paquete que consumirán otros desarrolladores. Si las pruebas fallan, entonces sabrá que sus métodos privados necesitan una mirada.
Brenden
2
El ejemplo de [wire] ( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go ) para la Estrategia 2, ahora también es un ejemplo de la Estrategia 1 ...
durp

Respuestas:

130

La diferencia fundamental entre las tres estrategias que ha enumerado es si el código de prueba está o no en el mismo paquete que el código bajo prueba. La decisión de utilizar package myfunco package myfunc_testen el archivo de prueba depende de si desea realizar pruebas de caja blanca o de caja negra .

No hay nada de malo en usar ambos métodos en un proyecto. Por ejemplo, podría tener myfunc_whitebox_test.goy myfunx_blackbox_test.go.

Comparación de paquetes de códigos de prueba

  • Prueba de caja negra: use package myfunc_test, lo que garantizará que solo esté usando los identificadores exportados .
  • Prueba de caja blanca: utilícela package myfuncpara tener acceso a los identificadores no exportados. Bueno para pruebas unitarias que requieren acceso a variables, funciones y métodos no exportados.

Comparación de las estrategias enumeradas en la pregunta

  • Estrategia 1: El archivo myfunc_test.gousa package myfunc: en este caso, el código de prueba myfunc_test.goestará en el mismo paquete que el código que se está probando myfunc.go, que se encuentra myfuncen este ejemplo.
  • Estrategia 2: El archivo myfunc_test.goutiliza package myfunc_test: en este caso, el código de prueba en myfunc_test.go"se compilará como un paquete separado y luego se vinculará y ejecutará con el binario de prueba principal". [Fuente: líneas 58 a 59 en el código fuente de test.go ]
  • Estrategia 3: el archivo myfunc_test.gousa package myfunc_testpero se importa myfuncusando la notación de puntos: esta es una variante de la Estrategia 2, pero usa la notación de puntos para importar myfunc.
Matthew Rankin
fuente
1
Cabe señalar que el uso de la Estrategia 1 también mantendrá los archivos _test.goseparados del paquete que se está probando (el mismo comportamiento que la Estrategia 2). Esto no parece estar documentado según github.com/golang/go/issues/15315
Kevin Deenanauth
Vi que un paquete sólido usaba la Estrategia 3, pero no puedo entender cuál es el punto.
PickBoy
1
Bifurqué un paquete e hice cambios, y ahora todas mis pruebas intentan importar el repositorio original en lugar de mi paquete bifurcado. Con la Estrategia 3, no tengo que cambiar "github.com/original/link" por "github.com/my/fork", porque solo hace referencia a '.' en lugar.
nmarley
1
@KevinDeenanauth Esto me sorprendió. Pensé que había encontrado un error cuando encontré un nombre que _test.gono era de _testpaquete y que contiene un func init()que cambia alguna variable de paquete global para probar. Estaba equivocado.
Zyl
1
@nmarley .no resuelve el problema de la bifurcación. No es una importación relativa. Simplemente importa los identificadores "en el paquete actual".
qaisjp
19

Depende del alcance de sus pruebas. Las pruebas de alto nivel (integración, aceptación, etc.) probablemente deberían colocarse en un paquete separado para asegurarse de que está utilizando el paquete a través de la API exportada.

Si tiene un paquete grande con muchos componentes internos que deben someterse a prueba, utilice el mismo paquete para sus pruebas. Pero esa no es una invitación para que sus pruebas accedan a ningún estado privado. Eso haría de la refactorización una pesadilla. Cuando escribo estructuras en go , a menudo estoy implementando interfaces. Son esos métodos de interfaz los que invoco de mis pruebas, no todos los métodos / funciones auxiliares individualmente.

mdwhatcott
fuente
13

Debe utilizar la estrategia 1 siempre que sea posible. Puede usar el foo_testnombre del paquete especial para evitar ciclos de importación, pero eso es mayormente allí, por lo que la biblioteca estándar se puede probar con el mismo mecanismo. Por ejemplo, stringsno se puede probar con la estrategia 1 ya que el testingpaquete depende de strings. Como dijiste, con la estrategia 2 o 3 no tienes acceso a los identificadores privados del paquete, por lo que normalmente es mejor no usarlo a menos que sea necesario.

guelfey
fuente
10
¿Cómo no tener acceso a identificadores privados en las pruebas no es una virtud?
Jub0bs
3
de acuerdo con las buenas prácticas de prueba, no prueba los detalles de la implementación interna para detectar un artefacto de código; haciendo hijo, es un código de olor
Gerardo Lima
0

Una nota importante que me gustaría agregar import .de Golang CodeReviewComments :

El import .formulario puede ser útil en pruebas que, debido a dependencias circulares , no pueden formar parte del paquete que se está probando:

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

En este caso, el archivo de prueba no puede estar en el paquete foo porque usa bar/testutil, que importa foo. Entonces usamos la 'importación'. formulario para permitir que el archivo pretenda ser parte del paquete foo aunque no lo sea.

Excepto en este caso, no lo utiliceimport . en sus programas. Hace que los programas sean mucho más difíciles de leer porque no está claro si un nombre como Quux es un identificador de nivel superior en el paquete actual o en un paquete importado.

Eric
fuente