Corriendo tests

En la vida del programador hay una tarea que lleva bastante tiempo, y es la de correr tests, ya sean "unit tests" (pruebas unitarias) o "integration tests" (pruebas donde se hacen interactuar subsistemas entre sí).

Es cierto, no todos los proyectos tienen tests, pero deberían. ¡Y son un vicio! Una vez que los probaste, querés pruebas en todos los proyectos. Pero claro, a los tests hay que correrlos, y hay muchas maneras de hacerlo.

La verdad es que la estructura de los tests es siempre la misma (o casi siempre), obviamente hablando de proyectos en Python, pero la forma de correrlos, y especialmente la forma de presentar los resultados, varía mucho de un corredor de tests a otros.

A lo largo de años he probado distintos, y debo decir que ninguno cumple 100% con lo que a mi me gustaría tener en el test runner ideal. Por otro lado, seguramente alguno (como nosetests, por ejemplo), cumpla gran porcentaje de lo que quiero, es cuestión de lograr lo que falta.

Acá está la listita de las cosas que cumpliría mi test runner soñado. Propuse un proyecto en el PyCamp de este mes para laburar en esto (obviamente no escribir algo desde cero, sino lograr el objetivo con el menor esfuerzo posible).

Le puse un número a cada ítem para que sea más fácil referenciar en cualquier discusión:

  1. Debería soportar que le pase un directorio (default a '.') y que descubra todo ahí y para abajo:

    $ <testrunner> project/tests/
    $ <testrunner>
    
  2. Debería soportar que le pase un archivo, y que corra sólo los tests de ese archivo:

    $ <testrunner> project/tests/test_stuff.py
    
  3. Debería soportar que le pase "paths de import de Python", y que corra sólo tests de ese paquete, módulo, clase, o lo que corresponda:

    $ <testrunner> project.tests
    $ <testrunner> project.tests.test_stuff
    $ <testrunner> project.tests.test_stuff.StuffTestCase
    $ <testrunner> project.tests.test_stuff.StuffTestCase.test_feature
    
  4. Debería poder pasarle una regex para que corra sólo lo que re.search() encuentra en el path completo del método:

    $ <testrunner> project/tests/ --search feature
        correría:
            test_feature
            test_feature_1
            test_feature_2
        no correría:
            test_crash
    
    $ <testrunner> project/tests/ --search feature$
        correría:
            test_feature
        no correría:
            test_feature_1
            test_feature_2
            test_crash
    
  5. Debería poder decirle que pare de correr los tests al encontrar el primer error o falla.

  6. Debería poder indicarle que mida los tiempos de cada test (y al final que presente un reporte con los N tests que más tardaron).

  7. Debería mostrar los resultados usando los nombres de paquete/módulo/clase/método, en una jerarquía de árbol o en la misma linea:

    $ <testrunner> project/tests/test_stuff.py
    project.tests.test_stuff
      StuffTestCase
        test_feature_1                      OK
        test_feature_2                    FAIL
      OtherStuffTestCase
        test_feature_A                      OK
    
    $ <testrunner> project/tests/test_stuff.py
    OK  project.tests.test_stuff.StuffTestCase.test_feature_1
    FAIL project.tests.test_stuff.StuffTestCase.test_feature_2
    OK  project.tests.test_stuff.OtherStuffTestCase.test_feature_A
    

    De cualquier manera, esto no afecta el órden de ejecución de las pruebas (secuencial, aleatoria, etc), sólo es cómo mostrar los resultados.

  8. Los OKs deberían ser verdes; ERRORs y FAILs deberían ser rojos.

  9. Los OKs/FAILs/ERRORs para cada prueba, en el listado, deberían estar alineados verticalmente.

  10. No debería capturar stdout/stderr.

  11. En el reporte final (luego del listado que va mostrando al ejecutar todo), debería mostrar el path completo del test que falla (o de los tests que fallan), junto con el (los) errores, de manera que si uno copia y pega ese path, sirva para correr ese único test.

Comentarios Imprimir

Ma, cosa facciamo?

En otras palabras, ¿qué ando haciendo? Como verán, escribiendo poco en el blog, :p

En parte la culpa la tiene que en el laburo hay mucho ídem (la semana que viene es el Mobile World Congress y estuvimos terminando unas cositas de lujo que se usan desde el Ubuntu Phone). Pero no sólo laburo es el tema... estoy empujando varios proyectos en paralelo, y la familia estuvo de vacaciones.

Bah, no toda la familia, Moni empezó a trabajar a principios de Febrero, Felu está en casa todo el día (al final este año no lo mandamos a colonia), y Male tuvo de vacaciones a la chica que la cuida a la mañana. Ergo, mis mañanas, durante un par de horas, eran complicadas. Esto en el trabajo lo compensaba trabajando más tiempo a la tarde, lo cual también me quitaba tiempo libre.

Male y Felu, jugando

Pero bien. Ya casi estamos en ritmo para el resto del año, así que quería contarles un poquito algunos avances en varios proyectos, que aunque no cerré ninguno como para contarlo por separado, está bueno que se vea el progreso.

Al que le metí más pata las últimas semanas es Encuentro. Más allá de algunas correcciones menores, estuve laburando para meterle un feature interesante: un nuevo backend de datos (que no voy a comentar hasta estar seguro que funca bien). Pero para eso necesitaba una biblioteca para parsear SWFs. Lo que encontré por ahí estaba roto o incompleto, y con el código muy ofuscado como para toquetearlo (no los culpo, parsear estructuras binarias es un dolor de oido, y la de SWF en particular es recontracomplicada). En fin, el tema es que necesité hacer la biblioteca yo. Y la hice, ¡muy divertido! Pero mucho trabajo. Acá está, para que la aprovechen si necesitan: Yet Another SWF Parser.

El otro proyecto de software que estuve empujando un poco es CDPedia, en dos frentes muy relacionados. Por un lado, tener un único ejecutable al que uno llame con varios parámetros y haga "todo solo" (hoy en día la generación completa de la CDPedia implica muchos pasos manuales). Por otro lado, que la generación sea en otro idioma que el castellano. Ambos frentes se juntan en la idea de tener una máquina que vaya generando sola CDPedias de distintos idiomas. Ya llegaremos.

También estuve un par de días agregándole un feature a Magicicada (que se iconice) y corrigiéndole un bug molesto (cada vez que una transferencia terminaba había un efecto feo con el porcentaje de las otras). El único detalle que le quiero agregar para que me sirva en todo lo que lo uso es tener un botón de "publicar", que me deje elegir un archivo y lo publique (linkeándolo desde el directorio de Ubuntu One si fuese necesario).

A Linkode, lo tengo abandonadazo, pero ya lo voy a retomar. Y también le estoy tratando de meter algo de tiempo a Preciosa, la aplicación de Martín para que no nos mientan con los precios y poder comprar más barato.

Para complicarme la vida, se me pinchó un disco de la compu (un Western Digital Caviar Blue de 500GB). Empezó a hacer ruidito, y podía acceder a veces sin problemas, y a veces se turulaba. Julia me dió la idea de apagarlo por software (hdparm -Y) para que no se siga deteriorando mientras obtenía el reemplazo, así que aguantó varias semanas sin problema. Finalmente compré un Seagate de 3TB, y el otro día lo reemplacé sin mayor inconveniente, pudiendo rescatar todos los datos (con el detalle, ojo al piojo, de que hay que crearle una tabla de particiones tipo GPT, porque la común que usamos toda la vida solamente sirve hasta 2TB).

Y en el medio de todo, cerrando el contrato con el hotel donde vamos a hacer el PyCamp este año. Es en Villa Giardino, como el año pasado, ¡y ya pueden anotarse! No dejen de venir, es el evento de programación más divertido y productivo del año, :)

En fin. ¿Qué ando haciendo? Mil cosas :D

Comentarios Imprimir

Descansando

Este enero con la familia nos tomamos unos días para pasear.

No teníamos ganas de ir mucho tiempo a un lugar puntual, así que hicimos una especie de popurrí.

El primer viajecito fue bastante cerca: nos fuimos a una chacra en Open Door por tres días. Era con las cuatro comidas incluídas, así que descansamos y comimos un montón...

Había una pileta que disfrutamos muchísimo, y actividades para los pequeños, que incluía darle de comer a animalitos, ordeñar una vaca, y varios etcéteras.

Viendo como se ordeñaba una vaca

El segundo viaje fue más lejos, e hicimos dos lugares en tándem. De casa fuimos para Costa Azul, a disfrutar de la playa y el mar.

Me sorprendió por un lado todo lo que Felu disfrutó el mar, y las ganas con las que Malena le entraba a la playa y el mar.

Malena y el mar

De la costa fuimos directamente para Chascomús, donde nos quedamos un par de días.

En el viaje nos agarró una tormenta como nunca había visto. En la vida había manejado con tanta agua alrededor. Pero llegamos bien, y como los días no estaban tan soleados, aprovechamos para descansar bastante.

El último día se acercaron mi vieja y Oscar y armamos un día de pesca en la laguna. Estaba fresco, pero soleado, y nos terminamos quemando un poco demasiado :/

Felipe en la laguna

En fin, descansamos mucho, y comimos más (ya nos pusimos a dieta :p). Y los chicos la pasaron bárbaro.

Comentarios Imprimir

Decoración coreana

En Londres estuvimos (con Guillo y Lucio) sacando varias fotos "pensadas"... o sea, elegíamos algo y tratábamos de sacar la mejor foto, cambiando parámetros de la cámara, etc.

Esta foto, sin embargo, fue sacada así de primera (pero no en modo automático, sino jugando con el tiempo de exposición), en el restaurant coreano al que fuimos a cenar una noche.

Decoración coreana
Comentarios Imprimir

Cada cosa en su lugar

Esta es una frase de Anthony Bourdain en uno de sus programas de No Reservations.

Mise en place: it's my religion, a fundamental principle, the Tao of cooking. It's the practice of setting up your personal station, having all your components ready, the amounts you need and where you want them, and all your favorite tools to grab at a moment's notice, without the burden of delay or thought.

Traducido libremente por mí, es:

Poner en lugar: es mi religión, un principio fundamental, el Tao de la cocina. Es la práctica de armar tu estación personal, teniendo todos los componentes listos, la cantidad que necesitás y donde los querés, y todas tus herramientas favoritas para usarlas al momento, sin tener que esperar y sin pensarlo.

Reconozco que aplico a diario el mismo concepto. En mi computadora, que es donde trabajo, para ser lo productivo que me gusta ser, tengo que tener un montonazo de pequeñas cosas bien ordenadas... o sea, aplicaciones instaladas y configuradas como a mí me gusta, archivos con datos donde los quiero tener, las ventanas acomodadas donde estoy acostumbrado, etc.

Por eso me cuesta tanto reinstalar el sistema operativo desde cero (lo hago cada par de años), porque me lleva un tiempo considerable el dejar "el entorno de trabajo como corresponde".

Comentarios Imprimir