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

Andrey Antukh niwi en niwi.be
Dom Ene 5 15:08:26 CET 2014


El 31 de diciembre de 2013, 10:06, Jesus Cea <jcea en jcea.es> escribió:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> 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.
>
> - --
> 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
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.4.15 (GNU/Linux)
> Comment: Using GnuPG with Thunderbird - http://www.enigmail.net/
>
> iQCVAwUBUsKJG5lgi5GaxT1NAQLu7wQAlG+qzPwUIZWi0wLtgXBg44WWMbODuqZd
> SQrsSxFxEL5GHhyWiW2RCJ7cG5B9Sgtbfg2Sez0o9PiAwFxMku42DxTJwS/tPTpK
> 15I9WUuvN2lylAOvMPvn5CuUsuis2wQ0R2hv5jgXPJ39Kl/e2ncwuiZB83J1APvd
> 5jZXkbYoTz8=
> =9H+8
> -----END PGP SIGNATURE-----
> _______________________________________________
> Python-es mailing list
> 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 Antukh - Андрей Антух - <niwi en niwi.be>
http://www.niwi.be/about.html
http://www.kaleidos.net/A5694F/

"Linux is for people who hate Windows, BSD is for people who love UNIX"
"Social Engineer -> Because there is no patch for human stupidity"
------------ próxima parte ------------
Se ha borrado un adjunto en formato HTML...
URL: <http://mail.python.org/pipermail/python-es/attachments/20140105/2a2dc7f6/attachment.html>


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