Si usted fué un fanatico del rock de los 80, seguro sabe quien es Mottley Crue y su clasico "Dr. FeelGood". ¿Que tiene que ver la adicción a la cocaina y las pruebas de unidad (unit tests) en su código? Bueno, en que la última es una buena adicción y ¡debería ser ilegal no hacerlo!

Si usted es un programador en Java, seguro que utiliza Ant en sus proyectos, para automatizar compilación, empaque e instalación (y si no, entonces sufre un caso severo de demencia o no le interesa hacer su vida más fácil). Lo otro es que seguramente usted detesta ver errores en su trabajo y cuando esta echando código, lo primero que hace es preparar sus pruebas de unidad (unit tests) utilizando JUnit.

No voy a entrar en detalles sobre como trabajar con Ant o JUnit, ya que hay excelentes tutoriales en Internet sobre el tema. Más bien le voy a dar dos trucos que me han resultado super útiles cuando estoy haciendo este tipo de cosas:

Truco #1: Mantenga dos directorios separados, uno para el código de producción y otro para las pruebas de unidad, pero el segundo imitando la misma jerarquia de paquetes del primero.

Si, suena complicado, pero mire el siguiente ejemplo:

src/java/main/
`-- com
  `-- kodegeek
    `-- blog
      |-- batch
      |  `-- geo
      |   |-- CSVAsParser.java
      |   |-- CSVGeoParser.java
      |   |-- CSVParser.java
      |   |-- GeoDataFinder.java
      |    `-- package.html
src/java/test/
`-- com
 `-- kodegeek
  `-- blog
   `-- batch
    `-- geo
     `-- TestGeoDataFinder.java

Ahh, ahora si me entiende. Note como las clases "GeoDataFinder" y "TestGeoDataFinder" pertenecen al paquete "com.kodegeek.blog.batch.geo". Pero al compilar, nuestras reglas de Ant quedan mucho más sencillas ya que no tenemos que excluir directorios.

Fijense en un extracto de como compilamos el código fuente:

<target name="build"
    depends="init"
    description="Compile the Java source code">

    <javac srcdir="${src}/main"
         destdir="${build}"
        deprecation="true"
        optimize="false"
        debug="true">

        <include name="com/kodegeek/blog/batch/geo/**/*.java"/>
        <classpath refid="path.classpath"/>
    </javac>
    <copy todir="${build}" overwrite="true">
        <fileset dir="${properties}/main">
            <include name="com/kodegeek/blog/batch/geo/**/*.properties"/>
        </fileset>
    </copy>
</target>

Y las pruebas de unidad:

<target name="test"
    depends="build"
    description="Unit testing">

    <javac srcdir="${src}/test"
        destdir="${test}"
        deprecation="true"
        optimize="false"
        debug="true">

        <include name="com/kodegeek/blog/batch/geo/**/*.java"/>
        <classpath refid="path.test"/>
    </javac>
    <copy todir="${build}" overwrite="true">
        <fileset dir="${src}/test">
            <include name="com/kodegeek/blog/batch/geo/**/*.properties"/>
        </fileset>
    </copy>
    .....
</target>

La única diferencia es la propiedad "srcdir". Primero compilamos el código fuente y luego el de las pruebas de unidad, y ¡listo!

Un arbol dice más que mil palabras :D

La ventaja es que usted puede acceder al estado interno de sus clases de producción, al mismo tiempo que la cantidad de código a echar es potencialmente menos (al menos son menos imports). En este caso se puede violar el principio de la "caja negra" de manera más fácil mientras probamos.

Truco #2: Cuando haga sus pruebas de unidad, dele al desarrollador la oportunidad de decidir si las quiere correr todas o una sola.

No hay nada más ladilla que tener que esperar correr 100 pruebas de unidad hasta que nos toca la que nos interesa (bueno, los discursos de los politicos Venezolanos son peores, pero aún no han inventado el software para borrarlos. ¿voluntarios?)

Para eso nos valemos de la propiedad "if". Simplemente si queremos correr todas las pruebas de unidad llamamos al target "test". Pero si quieremos correr solamente una de las pruebas de unidad, entonces le pasamos una propiedad a ant por la línea de comandos. Por ejemplo esta es la salida corriendo todas las pruebas de unidad:

[josevnz@localhost kodegeek]$ ant -f etc/build/GeoDataFinder/build.xml test
Buildfile: etc/build/GeoDataFinder/build.xml
sanity-checks:
     [echo] Sanity checks finished...
init:
build:
     [copy] Copying 1 file to /home/josevnz/sf/kodegeek/build/GeoDataFinder
test:
    [javac] Compiling 1 source file to /home/josevnz/sf/kodegeek/test/GeoDataFinder
    <strong>[junit] Running com.kodegeek.blog.batch.geo.TestCSVAsParser</strong>
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.106 sec
    <strong>[junit] Running com.kodegeek.blog.batch.geo.TestGeoDataFinder</strong>
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 34.624 sec
[junitreport] Transform time: 2881ms
BUILD SUCCESSFUL
Total time: 42 seconds
[josevnz@localhost kodegeek]$

Pero esta vez sólo queremos correr 'com.kodegeek.blog.batch.geo.TestGeoDataFinder'. No hay rollo:

[josevnz@localhost kodegeek]$ ant -f etc/build/GeoDataFinder/build.xml -Dtest.single=com.kodegeek.blog.batch.geo.TestGeoDataFinder test
Buildfile: etc/build/GeoDataFinder/build.xml
sanity-checks:
     [echo] Sanity checks finished...
init:
build:
     [copy] Copying 1 file to /home/josevnz/sf/kodegeek/build/GeoDataFinder
test:
    [junit] Running com.kodegeek.blog.batch.geo.TestGeoDataFinder
    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 74.55 sec
[junitreport] Transform time: 3083ms
BUILD SUCCESSFUL
Total time: 1 minute 20 seconds

¡Chevere!. Un pajazo, ¿no es así? Veamos el código que nos permite salirnos con la nuestra:

<junit printsummary="yes"
    haltonfailure="no"
    description="Unit tests"
    fork="yes">

    (quitando paja que no nos interesa....)
   
    <classpath refid="path.test"/>
   
    <formatter type="plain"/>
    <formatter type="xml"/>
   
    <!-- Allow the user to execute single tests if desired -->
    <test name="${test.single}"
        haltonfailure="no"
        outfile="${test}/reports/${test.single}"
        if="test.single">

    </test>
   
    <batchtest todir="${test}/reports" unless="test.single">
        <fileset dir="${test}">
            <include name="**/*Test*"/>
        </fileset>
    </batchtest>
 
</junit>   
 
<junitreport todir="${test}/reports">
    <fileset dir="${test}/reports">
        <include name="TEST-*.xml"/>
    </fileset>
    <report format="frames"
        todir="${test}/reports/html"/>

</junitreport>

En resumen: Es un buen habito el probar nuestro código con pruebas de unidad.

Que les puedo decir, es el tipo de cosas que te vienen a la cabeza cuando estás echando código al frente de tu computadora, escuchando Heavy Metal :D. Mejor me tomo un café a ver si termino este pedazo de código en el que estoy metido.

-KodeGeek.


7 Respuestas a “Dr. FeelGood: ¡Pruebas de unidad en todas tus clases!”

  1. 1 Aníbal Rojas

    ¡Excelente! Otra opción para automatizar el testing es utilizar algo como Maven (de Jakarta), aunque con Ant ya estás resolviendo.

  2. 2 Edgar González

    Los tests rulez!

    Totalmente de acuerdo en que el no uso de test debería ser ilegal :-P

    Ahora un punto con el que me he enfrentado al hacer test "funcionales" para aplicaciones J2EE es que se complica el asunto, en su momento intenté usar cactus (Jakarta) pero era complicado, ¿alguna idea en este sentido?

    Por cierto sino has usado Maven, deberías darle una probada, te facilita la vida en ciertas cosas, todavía no he probado el la versión 2, pero la versión 1.x funciona bastante bien, desde el soporte para testing, hasta la generación de "artefactos" como documentación, elaboración de métricas, deployments, etc.

  3. 3 camilo

    Me gustaria que me ayudaran sobre que son las pruebas de unidad y las de compatibilidad

  4. 4 pete loco

    porque no se van a cagar todos???

  5. 5 pete lococococ

    porque no se van a cagar todos???
    porque no se van a cagar todos???
    porque no se van a cagar todos???
    porque no se van a cagar todos???
    porque no se van a cagar todos???

  6. 6 pete lococococ

    alert('que olor a mierda que hay aca!!!');

  7. 7 pete lococococ

    s

Añade un Comentario





RSS feeds

Suscríbete a nuestros RSS Feeds