Tests de aceptación con Fitnesse


En un post anterior escribí sobre cómo escribir tests de aceptación con el framework Robot. En esta ocasión voy a escribir sobre otro framework con el mismo fin: Fitnesse.

Fitnesse es bastante diferente de Robot. Para empezar, porque no es sólo un framework, sino también un wiki... Podríamos decir que es una plataforma de testing.

Además, permite realizar los fixtures en casi cualquier lenguaje. En esta ocasión veremos sólo Java.

Preparando el entorno

Lo primero es descargarse Fitnesse. Éste consta de un único archivo .jar:

wget "http://www.fitnesse.org/fitnesse-standalone.jar?responder=releaseDownload&release=20150814" -O fitnesse-standalone.jar

Como es evidente, necesitaremos tener instalado Java. La versión dependerá: Si vamos a crear los fixtures en Java, necesitaremos el JDK, mientras que si los vamos a crear en otro lenguaje nos basta con el JRE.

Ya podemos lanzarlo:

java -jar fitnesse-standalone.jar -p 9000

Lo que levantará un servidor web con el wiki en el puerto 9000 (por defecto usaría el 80). Conectaos y navegad. Ahí está toda la documentación. Veréis que en algunas páginas se muestra un botón en la parte superior del tipo "Test" o "Suite"... Si lo pulsáis, se lanzan los tests de esa página o de todas las inferiores, respectivamente.

La primera vez que se ejecute descomprimirá todo el wiki para que esté disponible para su edición.

Ejecutando desde línea de órdenes

También se pueden lanzar los tests en línea de órdenes. Para ello se utiliza la opción -c y la ruta parcial a lanzar. A menudo será interesante añadir también &format=text para poder verlo bonito en modo texto:

# Lanzar todos los tests:
java -jar fitnesse-standalone.jar -c "FitNesse.SuiteAcceptanceTests?suite&format=text"
# Lanzar sólo los tests de comentarios:
java -jar fitnesse-standalone.jar -c "FitNesse.SuiteAcceptanceTests.SuiteWidgetTests.TestComment?suite&format=text"

Fitnesse lanzará todo lo necesario para terminar devolviéndonos el resultado.

Cómo funciona

Es importante tener claro cómo funciona Fitnesse, ya que esto condiciona hacer bien o mal los tests.

Por una parte está el wiki, que ya hemos visto y que tiene poco que explicar. Las páginas se pueden editar directamente desde él.

Por otro lado, el servidor de pruebas. Fitnesse lanzará uno por cada Suite a probar, y puede estar implementado en cualquier lenguaje. Éste se encargará de manejar las fixtures.

Finalmente, está el procesador, que puede ser Fit o Slim. Ambos son dos procesadores HTML que extraen los tests de las páginas HTML, los ejecutan en el servidor de pruebas y generan el HTML resultante. Dado que Slim es la evolución de Fit, me centraré sólo en Slim.

Slim permite definir variables y tablas. Para ello utiliza un lenguaje especial en el wiki. Por ejemplo, para definir una variable usa:

!define TEST_SYSTEM {slim}

Así se define la variable TEST_SYSTEM con el valor slim. Esta variable es muy importante, ya que define el sistema de test a utilizar en la Suite.

Para crear nuevas páginas en el wiki, basta con editar una página y escribir una palabra camelcase ("EstoEsUnEjemplo"); al guardar nos aparecerá el texto renderizado y una interrogación ("Esto Es Un Ejemplo [?]"). Al pulsar sobre ese link, se creará la página enlazada. Así de simple.

Tablas

Las tablas pueden definir distintas cosas, desde las librerías a importar a los propios tests. En esta ocasión nos centraremos en dos: Import Table y Decision Table.

Hay distintos formatos... Todos basados en CSV o TSV. Aquí utilizaré uno que parece una tabla, separando los campos por pipes ("|").

Import Table

Se utiliza para importar librerías:

|import|
|example.jar|
|org.magmax|

La primera línea describe el tipo de tabla, mientras que el resto expone las librerías a exportar. Esto generará HTML:

<table>
      <tbody><tr class="slimRowTitle">
              <td>import</td>
      </tr>
      <tr class="slimRowColor0">
              <td>example.jar</td>
      </tr>
      <tr class="slimRowColor1">
              <td>org.magmax</td>
      </tr>
</tbody></table>

Decision Table

Una tabla de Decisión nos permite ejecutar fixtures. Veremos aquí lo básico y más adelante tendremos ejemplos reales:

|Multiplication|
|factor|factor|multiply?|
|4|2|8|
|44|12|8|

Las Decision Table son las tablas por defecto, así que no es necesario indicarlo. La primera línea indica la clase a utilizar como fixture; la segunda indica qué métodos invocar; a partir de la tercera muestra los valores a utilizar para cada método.

La columna que muestra una interrogación implica

Se creará una instancia de la clase indicada por cada fila. Dicho de otro modo: cada fila a partir de la tercera es un test.

Fixtures

Finalmente llegamos a las Fixtures, que son el corazón de todo. El problema aquí es que dependen del lenguaje, así que veremos opciones en Java, cada cual con sus ventajas e inconvenientes.

Java

Una Fixture Java no es más que una clase con unos métodos. Por ejemplo, para probar la clase anterior necesitaremos la clase Multiplication con los métodos void setFactor(Object a) y Object multiply(). Todas las columnas que no acaben con interrogación buscarán el método set correspondiente.

Veamos un ejemplo de implementación (archivo java/multiplication.java):

package org.magmax;

import java.util.ArrayList;
import java.util.List;


public class Multiplication {
    private ArrayList<Integer> factors = new ArrayList<Integer>();

    public void setFactor(int n) {
        factors.add(n);
    }

    public long multiply() {
        long result = 1;
        for (Integer i: factors) {
            result *= i;
        }
        return result;
    }
}

Es fácil de explicar: Cada objeto se guarda una lista de factores que se multiplican en el método multiply. Podemos compilarla:

javac multiplication.java

Y crear el wiki necesario:

!define TEST_SYSTEM {slim}

!path java

|import|
|example.jar|
|org.magmax|

|Multiplication|
|factor|factor|multiply?|
|4|2|8|
|44|12|8|

|Multiplication|
|factor|factor|factor|multiply?|
|2|2|2|8|

Obteniendo algo como lo siguiente:

Ejemplo de salida de Fitnesse

En este ejemplo podemos ver varios tests que pasan y uno que falla, debido a que el test está mal especificado.

Python

Pues... Sinceramente, ha sido un verdadero dolor.

La opción de usar Fit es sencilla, pero claro, carecemos de la mayor parte de las características molonas. Basta con instalar el paquete pyfit.

Para usar Slim es necesario instalar el paquete waferslim, cuyo paquete pip simplemente no funciona. Tras no pocos problemas, tanto con python 2.7 como con python 3.4, he tenido que añadir algún parche que otro y forkear una rama que no es la oficial. Así que recomiendo optar por la solución fácil:

  1. En el mismo directorio en el que tengáis el jar de Fitnesse, clonad mi repositorio: git clone https://github.com/magmax/waferslim.git

  2. Cread un wiki nuevo con este contenido (llamadlo, por ejemplo, EjemploPython):

    !define TEST_SYSTEM {slim}
    
    !path .:fixtures
    !define COMMAND_PATTERN {python -m waferslim.server -s %p -p }
    
    |Import|
    |multiplication.py|
    
    |Multiplication|
    |factor|factor|multiply?|
    |4|2|8|
    |44|12|8|
    
    |Multiplication|
    |factor|factor|factor|multiply?|
    |2|2|2|8|
    
  3. Ahora cread el archivo fixtures/multiplication.py, que es el que hemos importado antes:

    class Multiplication(object):
        factors = []
    
        def setFactor(self, f):
            self.factors.append(int(f))
    
        def multiply(self):
            result = 1
            for x in self.factors:
                result *= x
            return result
    
  4. Ejecutad los tests

Lejos de estar perfecto y mosqueado por usar un entorno de tests que no tiene tests... Pero por lo menos "parece" que funciona.

Ventajas e inconvenientes

Suponiendo que Fitnesse funcionara con Python, ambos tendrían sus pros y sus contras:

LANGUAGE PROS CONS
Java Todo Java; no es necesario instalar nada Hay que compilar las fixtures
Python No hay que estar compilando las fixtures. Requiere un intérprete python y hay que estar instalando librerías adicionales Requiere un intérpreter python y hay que estar instalando librerías adicionales.

Conclusión

Robot no es el único framework para hacer tests de aceptación, y puede ser muy buena opción en algunos casos. Fitnesse es otra opción bastante buena que admite muchas más opciones que Robot. Estas opciones pueden no ser necesarias, lo que redunda en complejidad innecesaria.

Para tests en Python, Robot es nativo; sin embargo, Fitnesse es más versátil y admite casi cualquier lenguaje.

Metiéndonos en el lenguaje, Robot es más orientado al script, mientras que Fitnesse genera tablas bonitas y fáciles de enseñar a "los jefes".

Robot tiene otras ventajas, como el lenguaje intermedio de resultados. Se puede utilizar para operaciones avanzadas: Reintentar tests fallidos, paralelizar tests... Estas opciones son más complejas con Fitnesse.

Próximamente hablaré sobre Concordion. Aunque ya escribí algo anteriormente sobre Concordion, espero terminar esta serie de artículos con otro más sobre este framework, entrando en mucho mayor detalle.


Comentarios

Comments powered by Disqus