Estoy aprendiendo Go codificando un pequeño proyecto personal. Aunque es pequeño, decidí hacer pruebas de unidad rigurosas para aprender buenos hábitos en Go desde el principio.
Las pruebas unitarias triviales estaban bien y dandy, pero ahora estoy desconcertado con las dependencias; Quiero poder reemplazar algunas llamadas a funciones con simulacros. Aquí hay un fragmento de mi código:
func get_page(url string) string {
get_dl_slot(url)
defer free_dl_slot(url)
resp, err := http.Get(url)
if err != nil { return "" }
defer resp.Body.Close()
contents, err := ioutil.ReadAll(resp.Body)
if err != nil { return "" }
return string(contents)
}
func downloader() {
dl_slots = make(chan bool, DL_SLOT_AMOUNT) // Init the download slot semaphore
content := get_page(BASE_URL)
links_regexp := regexp.MustCompile(LIST_LINK_REGEXP)
matches := links_regexp.FindAllStringSubmatch(content, -1)
for _, match := range matches{
go serie_dl(match[1], match[2])
}
}
Me gustaría poder probar el descargador () sin obtener una página a través de http, es decir, burlándose de get_page (más fácil ya que devuelve solo el contenido de la página como una cadena) o http.Get ().
Encontré este hilo: https://groups.google.com/forum/#!topic/golang-nuts/6AN1E2CJOxI que parece estar relacionado con un problema similar. Julian Phillips presenta su biblioteca, Withmock ( http://github.com/qur/withmock ) como una solución, pero no puedo hacer que funcione. Estas son las partes relevantes de mi código de prueba, que para mí es en gran medida código de culto de carga, para ser honesto:
import (
"testing"
"net/http" // mock
"code.google.com/p/gomock"
)
...
func TestDownloader (t *testing.T) {
ctrl := gomock.NewController()
defer ctrl.Finish()
http.MOCK().SetController(ctrl)
http.EXPECT().Get(BASE_URL)
downloader()
// The rest to be written
}
La salida de prueba es la siguiente:
ERROR: Failed to install '_et/http': exit status 1
output:
can't load package: package _et/http: found packages http (chunked.go) and main (main_mock.go) in /var/folders/z9/ql_yn5h550s6shtb9c5sggj40000gn/T/withmock570825607/path/src/_et/http
¿Withmock es una solución a mi problema de prueba? ¿Qué debo hacer para que funcione?
fuente
Respuestas:
¡Felicitaciones por practicar buenas pruebas! :)
Personalmente, no uso
gomock
(ni ningún marco de burla para el caso; burlarse de Go es muy fácil sin él). O pasaría una dependencia a ladownloader()
función como parámetro, o haríadownloader()
un método en un tipo, y el tipo puede contener laget_page
dependencia:Método 1: Pase
get_page()
como parámetro dedownloader()
Principal:
Prueba:
Método2: hacer
download()
un método de un tipoDownloader
:Si no desea pasar la dependencia como parámetro, también puede hacer
get_page()
un miembro de un tipo, y hacerdownload()
un método de ese tipo, que luego puede usarget_page
:Principal:
Prueba:
fuente
Si cambia la definición de su función para usar una variable en su lugar:
Puedes anularlo en tus pruebas:
Sin embargo, tenga cuidado, ¡sus otras pruebas podrían fallar si prueban la funcionalidad de la función que anula!
Los autores de Go usan este patrón en la biblioteca estándar de Go para insertar ganchos de prueba en el código para facilitar las pruebas:
https://golang.org/src/net/hook.go
https://golang.org/src/net/dial.go#L248
https://golang.org/src/net/dial_test.go#L701
fuente
Estoy usando un enfoque ligeramente diferente donde los métodos de estructura pública implementan interfaces, pero su lógica se limita a envolver funciones privadas (no exportadas) que toman esas interfaces como parámetros. Esto le brinda la granularidad que necesitaría para burlarse de prácticamente cualquier dependencia y, sin embargo, tener una API limpia para usar desde fuera de su conjunto de pruebas.
Para comprender esto, es imprescindible comprender que tiene acceso a los métodos
_test.go
no exportados en su caso de prueba (es decir, desde dentro de sus archivos), por lo que debe probarlos en lugar de probar los exportados que no tienen lógica dentro del envoltorio.Para resumir: ¡ pruebe las funciones no exportadas en lugar de probar las exportadas!
Hagamos un ejemplo. Digamos que tenemos una estructura Slack API que tiene dos métodos:
SendMessage
método que envía una solicitud HTTP a un webhook de SlackSendDataSynchronously
método que da una porción de cadenas itera sobre ellas y requiereSendMessage
cada iteraciónEntonces, para probar
SendDataSynchronously
sin hacer una solicitud HTTP cada vez que tendríamos que burlarnosSendMessage
, ¿verdad?Lo que me gusta de este enfoque es que al observar los métodos no exportados puede ver claramente cuáles son las dependencias. Al mismo tiempo, la API que exporta es mucho más limpia y tiene menos parámetros que transmitir, ya que la verdadera dependencia aquí es solo el receptor principal que está implementando todas esas interfaces. Sin embargo, cada función depende potencialmente solo de una parte de ella (una, quizás dos interfaces), lo que hace que los refactores sean mucho más fáciles. Es agradable ver cómo su código está realmente acoplado con solo mirar las firmas de funciones, creo que es una herramienta poderosa contra el mal olor del código.
Para facilitar las cosas, pongo todo en un archivo para permitirle ejecutar el código en el patio de recreo aquí, pero le sugiero que también consulte el ejemplo completo en GitHub, aquí está el archivo slack.go y aquí el slack_test.go .
Y aquí todo :)
fuente
Haría algo como
Principal
Prueba
Y lo evitaría
_
en golang. Mejor usar camelCasefuente
p := patch(mockGetPage, getPage); defer p.done()
. Soy nuevo para ir, y estaba tratando de hacer esto usando launsafe
biblioteca, pero parece imposible hacerlo en el caso general.Advertencia: Esto podría inflar un poco el tamaño del archivo ejecutable y costar un poco de rendimiento en tiempo de ejecución. En mi opinión, esto sería mejor si Golang tiene características como macro o decorador de funciones.
Si desea burlarse de las funciones sin cambiar su API, la forma más fácil es cambiar un poco la implementación:
De esta manera, podemos burlarnos de una función de las otras. Para que sea más conveniente, podemos proporcionar una placa repetitiva de este tipo:
En archivo de prueba:
fuente
Teniendo en cuenta que la prueba unitaria es el dominio de esta pregunta, le recomiendo que use https://github.com/bouk/monkey . Este paquete te hace simular pruebas sin cambiar tu código fuente original. Compare con otra respuesta, es más "no intrusivo"。
PRINCIPAL
SIMULACROS DE EXAMEN
El lado malo es:
- Recordado por Dave.C, este método no es seguro. Así que no lo use fuera de la prueba unitaria.
- No es idiomático Go.
El buen lado es:
++ No es intrusivo. Hacerte hacer cosas sin cambiar el código principal. Como dijo Thomas.
++ Hace que cambie el comportamiento del paquete (tal vez proporcionado por un tercero) con menos código.
fuente