[Python-es] ¿Algún recurso sobre trucos prácticos y ejemplos realistas de testing?

Jesus Cea jcea en jcea.es
Jue Abr 24 00:50:03 CEST 2014


Repito el mensaje completo porque han pasado casi 5 meses desde el hilo
original y habrá gente que no lo haya leído. Comentarios al final.


On 05/01/14 15:08, Andrey Antukh wrote:
> 
> 
> 
> El 31 de diciembre de 2013, 10:06, Jesus Cea <jcea en jcea.es
> <mailto:jcea en jcea.es>> escribió:
> 
> Una cosa que he ido descubriendo con los años es que para poder hacer
> buenos tests es conveniente que lo que estés probando se preste a
> ello. Tienes que programar de forma que lo que haces sea fácilmente
> testable.
> 
> El enfoque habitual en otros lenguajes, y mi tendencia en el pasado es
> utilizar inyección de dependencias. De hecho hubo una época en la que
> di la lata un poco para incluir inyección de dependencias en Python,
> pero la respuesta masiva fue que eso era antipitónico y que el futuro
> eran los mocks y similares.
> 
> Pero me encuentro que para probar una rutina de 20 lineas escribo 200
> lineas de tests más complejos que la propia rutina a comprobar, el
> desarrollo es lento y no puedo evitar pensar que estoy haciendo algo
> mal :-).
> 
> Así que, ¿alguien conoce recursos online con consejos prácticos y
> ejemplos realistas?. Porque hacer un chequeo mínimamente completo de
> esta rutina está siendo un infierno.
> 
> Esta rutina genera una clave al azar e intenta registrarla en un
> servidor, que devuelve un 401 mientras un administrador no ha admitido
> el registro (y en ese caso devuelve un 200). Una vez que tenemos el
> 200, nos guardamos ese usuario y clave en disco y no necesitamos
> repetir la operación.
> 
> """
> def consigue_autenticacion() :
>     # Creamos el fichero si es preciso
>     open("/tmp/heartbeat", "w").close()
>     #os.utime("/tmp/heartbeat")
> 
>     try :
>         with open("/local/auth", "r") as f :
>             token = f.read().strip()
>         if " " in token :
>             return  # Ya tenemos usuario y clave
>     except FileNotFoundError :
>         with open("/dev/urandom", "rb") as f :
>             token = f.read(4096)
>         if len(token) != 4096 :
>             raise RuntimeError("Lectura parcial de entropía")
>         token = md5(token).hexdigest()
>         with open("/local/auth", "w") as f :
>             f.write(token+"\n")
> 
>     usuario = "XXXXXX"
>     clave = "XXXXXXXX"  # Confidencial, pero no crítico
>     auth = requests.auth.HTTPBasicAuth(usuario, clave)
> 
>     addr = netifaces.ifaddresses("eth0")
>     ip_addr = addr[netifaces.AF_INET][0]["addr"]
>     mac_addr = addr[netifaces.AF_LINK][0]["addr"]
>     mac_addr = mac_addr[0:2]+mac_addr[3:5]+mac_addr[6:8]+ \
>                 mac_addr[9:11]+mac_addr[12:14]+mac_addr[15:17]
> 
>     factor = 1*60
>     while True :
>         t = time.time()
>         respuesta = requests.get("https://XXXXXX.jcea.es/registro?"
>                 "ip_addr=%s&mac_addr=%s&ts=%.0f" \
>                         %(ip_addr, mac_addr, time.time()),
>              auth = auth,
>              verify = "XXXXXXXXX.jcea.es.cert",
>              timeout = 1*60,
>              headers = {"clave": token})
>         os.utime("/tmp/heartbeat")
>         if respuesta.status_code == 401 :
>             t = t + factor
>             factor = factor * 3
>             if factor > 3600 :
>                 factor = 3600
>             while time.time() < t :
>                 time.sleep(10)
>                 os.utime("/tmp/heartbeat")
>             continue  # Volvemos a intentarlo
>         elif respuesta.status_code == 200 :
>             with open("/local/auth", "w") as f :
>                 f.write(mac_addr+" "+token+"\n")
>             return
>         else :
>             raise RuntimeError("El servidor nos devuelve un status %s" \
>                     %respuesta.status_code)
> """
> 
> El código de testeo de esta rutina es complicado de cojones, feo,
> frágil. Uso Mocks para comprobar que las llamadas se realizan en el
> orden y con los parámetros correctos, tiro excepciones, simulo
> ficheros, etc. La pruebo a fondo. Pero desarrollar el test ha sido
> costosísimo.
> 
> ¿Consejos?.
> 
> Se admiten recomendaciones de libros.
> 
>     _______________________________________________
>     Python-es mailing list
>     Python-es en python.org <mailto:Python-es en python.org>
>     https://mail.python.org/mailman/listinfo/python-es
>     FAQ: http://python-es-faq.wikidot.com/
> 
> 
> 
> Hola.
> 
> La rutina que presentas, es obviamente muy complicada de testear, pero
> por que? veamos:
> 
> * Demasiada logica, la funcion es demasiado grande y hace demasiadas
> cosas de bajo nivel
> * * La logica de generar token tiene que ser otra funcion, que puda ser
> mockeada (con un mock tonto de una lambda que devuelve un token dymmy)
> * * http, tiene que ser otro metodo que abstraiga de http, tu funcion no
> tiene que saber de http, tiene que saber solo de lo que hace, autenticar.
> * * la parte de  netinterfaces tambien tiene que ser abstraida.
> 
> 
> Si te fijas, en esta rutina tienes 0 de abstraccion, usas todas las
> bibliotecas tal cual con toda la logica adicional que eso implica,
> moquear eso es un infierno, pero si ocultas diferentes partes de la
> logica con una responsabilidad unica en distintas funciones, probar la
> logica concreta de esa rutina (que es seguir unos pasos concretos
> llamando a otras rutinas) seria crear 4 mocks llamables  de una sola linea.
> 
> La idea final es desacoplar.
> 
> Espero haberme explicado.
> Si ves que entenderás mejor con un ejemplo de como haria esta rutina,
> puedo hacerte un ejemplo!
> 
> Un saludo.
> Andrey

Andrey, tu comentario es magnífico. Gracias.

La clave, como dices, está en desacoplar, aunque en muchos casos es
bastante difícil, como cuando estás probando hardware y no puedes
saltarte pasos. Pero toda esta reflexión me ha resultado muy útil, estoy
modificando mi estilo para que sea más "comprobable". Muchas gracias por
tu mensaje, me ha resultado útil e inspirador.

-- 
Jesús Cea Avión                         _/_/      _/_/_/        _/_/_/
jcea en jcea.es - http://www.jcea.es/     _/_/    _/_/  _/_/    _/_/  _/_/
Twitter: @jcea                        _/_/    _/_/          _/_/_/_/_/
jabber / xmpp:jcea en jabber.org  _/_/  _/_/    _/_/          _/_/  _/_/
"Things are not so easy"      _/_/  _/_/    _/_/  _/_/    _/_/  _/_/
"My name is Dump, Core Dump"   _/_/_/        _/_/_/      _/_/  _/_/
"El amor es poner tu felicidad en la felicidad de otro" - Leibniz

------------ próxima parte ------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 538 bytes
Desc: OpenPGP digital signature
URL: <http://mail.python.org/pipermail/python-es/attachments/20140424/3b90365d/attachment.sig>


Más información sobre la lista de distribución Python-es