Testeando código con doctest en los comentarios
Ya estamos llegando al final de la guía Python! En el capítulo de hoy, "atacaremos" con una técnica de programación indispensable, que nos dará una gran ventaja: evitar "romper" el código con la incorporación de uno nuevo y prevenir bugs. Y ¿de qué técnica hablamos? Unit Testing o Test Unitarios.
Ya estamos llegando al final de la guía Python!
En el capítulo de hoy, “atacaremos” con una técnica de programación indispensable, que nos dará una gran ventaja: evitar “romper” el código con la incorporación de uno nuevo y prevenir bugs. Y ¿de qué técnica hablamos? Unit Testing o Test Unitarios.
Test Unitarios
Los test unitarios representan un mecanismo indispensable, para probar el funcionamiento individual, de cada parte del código, previniendo que el agregado de nuevo código, haga fallar el existente.
Changelog del capítulo VII
A continuación un resumen de los cambios que veremos:
capitulo6/constantes.py Nueva constante no pública capitulo6/helpers.py Agregados test con doctest nueva función para guardar archivos capitulo6/presupuesto.py Refactoring: guardar_presupuesto() utiliza función genérica para guardar capitulo6/recibo.py Refactoring: guardar_recibo() utiliza función genérica para guardar capitulo6/run.py refactoring: validación argumentos recibidos capitulo6/test/ carpeta que contiene los test del capítulo
Hoy, centraremos nuestra atención, haciendo énfasis en los cambios realizados al archivo helpers.py. Comencemos!
doctest
doctest es un módulo nativo de Python, que busca en los comentarios de nuestro código, fragmentos que se vean como sesiones del intérprete interactivo de Python, y procede a ejecutar dichos fragmentos, verificando que resulten como se le ha indicado.
Esto significa, que importando el módulo doctest, éste, buscará en los comentarios de nuestro código, todo fragmento que represente al interprete interactivo, para luego ejecutarlo. Por ejemplo:
import doctest def sumar_dos_numeros(a, b): """Suma dos números y retorna su resultado Argumentos: a -- primer sumando b -- segundo sumando Test: >>> sumar_dos_numeros(25, 10) 35 >>> sumar_dos_numeros(30, 20) 50 """ resultado = a + b print a + b if __name__ == "__main__": doctest.testmod()
Si vemos el texto debajo de “Test:”, luce como el intérprete interactivo.
Aquí estoy invocando a la función:
>>> sumar_dos_numeros(25, 10)
Aquí, estoy “simulando” el resultado que arrojaría en el intérprete interactivo. Esto, será interpretado por doctest, como “el resultado esperado”:
35
Y finalmente, verifico que el módulo esté siendo ejecutado como script (no importado), de ser así, doy la orden a doctest de ejecutar el script en modo “test”:
if __name__ == "__main__": doctest.testmod()
Colocando los “doctest” en un archivo independiente
En nuestro ejemplo (archivo helpers.py), sin embargo, nos encontramos con estas líneas:
74 if __name__ == "__main__": 75 doctest.testfile('tests/helpers.txt')
En este caso, lo que estamos haciendo, es indicar a doctest, que nuestras pruebas se encuentran en un archivo a parte: tests/helpers.txt Si abrimos este archivo, podremos ver todos los test:
1 >>> from helpers import leer_archivo, crear_archivo, mostrar_texto 2 3 Probando leer_archivo() 4 5 >>> leer_archivo('') 6 'Error' 7 8 >>> leer_archivo('tests/archivo_de_prueba.txt') 9 'Archivo de Prueba\nHola Mundo\n' 10 11 Probando crear_archivo() 12 13 >>> crear_archivo('', 'contenido') 14 'Error' 15 >>> crear_archivo('', '') 16 'Error' 17 >>> crear_archivo('tests/archivo_de_prueba.txt', '') 18 'Error' 19 >>> crear_archivo('tests/archivo_de_prueba.txt', 'Archivo de Prueba\nHola Mundo\n') 20 21 22 Probando mostrar_texto() 23 24 >>> mostrar_texto('Hola Mundo') 25 Hola Mundo 26 >>> mostrar_texto() 25
Si te fijas las líneas resaltadas en gris, podrás ver las llamadas a los métodos que estamos testeando. Mientras que las resaltadas en negro, simulan el resultando esperado.
El texto que no aparece resaltado, es interpretado como parte de los comentarios, exceptuando la línea 1, que se encarga de importar los métodos a ser testeados.
Ejecutando los test
Una vez que el código fuente cuenta con los correspondientes test, es hora de correrlos. Para ello, en la línea de comandos, escribiremos:
python nombre_del_modulo_a_testear.py -v
En nuestro caso, navegaremos hasta la carpeta capitulo6 y escribiremos
python helpers.py -v
Cuando ejecutemos los test, veremos una salida similar a la siguiente:
Trying: from helpers import leer_archivo, crear_archivo, mostrar_texto Expecting nothing ok Trying: leer_archivo('') Expecting: 'Error' ok Trying: leer_archivo('tests/archivo_de_prueba.txt') Expecting: 'Archivo de Prueba\nHola Mundo\n' ok Trying: crear_archivo('', 'contenido') Expecting: 'Error' ok Trying: crear_archivo('', '') Expecting: 'Error' ok Trying: crear_archivo('tests/archivo_de_prueba.txt', '') Expecting: 'Error' ok Trying: crear_archivo('tests/archivo_de_prueba.txt', 'Archivo de Prueba\nHola Mundo\n') Expecting nothing ok Trying: mostrar_texto('Hola Mundo') Expecting: Hola Mundo ok Trying: mostrar_texto() Expecting nothing ok 1 items passed all tests: 9 tests in helpers.txt 9 tests in 1 items. 9 passed and 0 failed. Test passed.
En lo anterior, “Trying” nos describe el código que se está ejecutando, mientras que “Expecting”, el resultado esperado. Si todo sale bien, concluirá el bloque indicando “ok”.
Al final del test, se puede acceder al reporte completo:
1 items passed all tests: 9 tests in helpers.txt 9 tests in 1 items. 9 passed and 0 failed. Test passed.
Solución del desafío del capítulo anterior
En el segundo desafío del capítulo anterior, la propuesta era pensar como podría evitarse la redundancia de código, en los métodos encargados de guardar los recibos y los presupuestos en formato HTML.
Siguiendo la línea inicial, de convertir en “ayudantes genéricos” aquellos métodos sin relación directa con el compartamiento del objeto en sí mismo, se creó un helper, para guardar dichos archivos:
47 def crear_archivo(ruta, contenido): 48 """crea un archivo en la ruta pasada con el contenido indicado 49 50 Argumentos: 51 ruta -- ruta al archivo. Ej. carpeta/archivo.txt 52 contenido -- template renderizado 53 54 """ 55 if not ruta or not contenido: 56 return 'Error' 57 else: 58 archivo = open(ruta, 'w') 59 archivo.write(contenido) 60 archivo.close()
Anexo de material de lectura complementario
El desafío de hoy, no dependerá de “resolver” un problema ni de encontrar ningún tipo de solución puntual. El reto de hoy, consiste en desafiarte a ti mismo:
Nunca terminas de aprender y jamás es suficiente lo que te enseñan.
Programar, no se limita a conocer la sintaxis y funcionamiento de un lenguaje de programación. Siempre podrás comenzar por una guía de aprendizaje, para conocer los caminos que puedes seguir, para convertirte en un verdadero profesional en determinada materia. Pero eso, no debe significarlo todo.
Solo lograrás ser un experto, en el momento en el que descubras, que jamás es suficiente lo que conoces y que el conocimiento no te otorga sabiduría.
Tienes una alternativa: no limitar tus recursos al mero conocimiento. No acotar tu carrera profesional al conocer sobre un lenguaje de programación.
De tu voluntad, dependerá el nivel que alcances, y de tu pasión, el superarte cada día.
A continuación, encontrarás un listado de recurso, que no puedes dejar de leer. La gran mayoría se encuentran en español. Las identificadas como “indispensables”, te recomiendo que hagas todo lo posible por leerlas. Las “recomendadas”, significa que “sería una buena idea” leerlas. Y las “complementarias”, dependerán de tu curiosidad.
El camino correcto…
Recurre a la razón, para asimilar lo que lees. Pero recurre a tus emociones, para saber si lo que haces, realmente te apasiona. Solo así, sabrás que estás siguiendo el camino correcto.
¡Disfruta la lectura y apasiónate practicando!
Lectura indispensable (ES)
Guía de aprendizaje de Python por Guido van Rossum (creador de Python)
Excelente material de referencias básicas del lenguaje.
Inmersión en Python (Dive into Python) por Mark Pilgrim
Excelente material para conocer a fondo el lenguaje y su aplicación en la programación orientada a objetos.
Python no muerde por Roberto Alsina
Excelente libro para aprender a razonar más allá del lenguaje.
Lecturas recomendadas (en español)
Tutorial Django por @cibernatural
Una muy buena guía para entender el funcionamiento del framework Django, ideal para crear aplicaciones Python Web based.
Tutorial de PyGTK por John Finlay
Tutorial oficial de PyGTK, una suite de módulos Python para crear aplicaciones de escritorio con interfaces gráficas GTK.
Lecturas complementarias (en español)
Aprenda a Pensar como un Programador con Python por Allen Downey, Jeffrey Elkner y Chris Meyers
Un libro extenso, para leer con tiempo y calma. Muy buen material para utilizar como referencia del lenguaje. Los últimos capítulos, hacen mayor énfasis en la programación orientada a objetos.
Lecturas recomendadas (EN)
MySQLdb User’s Guide por Andy Dustman
Guía oficial de MySQLdb, interface para trabajar con bases de datos MySQL desde Python.
Python doctest por Guido Van Rossum
Documentación oficial de Python doctest
The PyQT Tutorial por ZetCode
Tutorial sobre PyQT para crear aplicaciones de escritorio con interfaces gráficas (un especial agradecimiento Jorge Courbis por pasarme el link)
El final de la guía??? Creí que iban a ser mas de 20 capitulos……XELENTE TRABAJO
creo q iban hacer 10 capitulos. debo hacer de conocimiento, q esta guia me ayudó en mi aprendizaje de Python. gracias
Hola gume! tenemos algo más para el final, pero aún no hemos llegado. Estamos llegando, pero queda algo 😉
hola pepe! lo mismo que le dije a gume: aún falta algo más
Definitivamente con esta guia he aprendido mucho mas de lo que habia leido (como han sido en ingles los sitios y libros, no los comprendia al 100% xD) , muchas gracias por esta guia eugenia, y creo que si haces cursos de python, si los pagaria ya que explicas muy bien, seria un curso rico en informacion.
=3 espero que lo ultimo que queda sea de GUI, ya que solo se manejar tkinter en python.
Siempre lo diré..!!! ESTA GUIA ES GENIAL !!!!, y el reto de hoy está muy bueno.. creo que uno nunca termina de aprender y que cada cosa que se aprende es como entrar a un mundo nuevo.. wow… sinceramente.. lo mejor.. saludos a todos….
[…] capítulos se titula: TESTEANDO CÓDIGO CON DOCTEST EN LOS COMENTARIOS y como bien describe Eugenia: En el capítulo de hoy, “atacaremos” con una técnica de […]
Aca e aprendido un sin fin de cosas ahora me queda en mi seguir aprendiendo y Excelentisima esta la guía felicitaciones
Hola EU, espero que estes bien, una vez mas gracias por tu dedicacion, me gustaria saber en tu opinion cual es la mejor herrmienta para hacer GUI en python!!
Espero que estas lecciones no se acaben… de antemano gracias!!
Buen contenido, espero que sigan adelante, ayuda bastante a los principiantes como yo…saludos!!!
[…] es una interfaz para trabajar con bases de datos MySQL desde Python. El capítulo Nº7 de esta guía, se ha indicado como lectura recomendada, el Manual oficial de MySQLdb el cual […]
[…] Testeando código con doctest en los comentarios […]
[…] es una interfaz para trabajar con bases de datos MySQL desde Python. El capítulo Nº7 de esta guía, se ha indicado como lectura recomendada, el Manual oficial de MySQLdb el cual […]
[…] es una interfaz para trabajar con bases de datos MySQL desde Python. El capítulo Nº7 de esta guía, se ha indicado como lectura recomendada, el Manual oficial de MySQLdb el cual […]
[…] Bahit para Maestros del Web.Agrega tu comentario | Enlace permanente al […]
Gracias por tu tiempo y paciencia para lograr que estos cursos sean cada vez mejores, realmente seguire estudiando el contenido sugerido, sin embargo creo que extra;aremos los cursos que han estado excepcionales, un saludo desde Guatemala!