Aserciones más claras

Desde hace tiempo el uso de pruebas unitarias (unit tests) es algo crucial y cada vez más extendido en todos los proyectos que veo, tanto en proyectos de código abierto como en proyectos empresariales cerrados. Esto hace que el código de test cada vez sea más y más revisado y mantenido.

Una cosa importante para conseguir un código mantenible es que sea lo más legible posible. Como todos sabéis una parte imprescindible de las pruebas unitarias es la aserción de lo que espera la prueba. Cuando la complejidad del código aumenta también lo hacen la complejidad de las pruebas y hoy os vamos a presentar una herramienta para intentar hacer menos complejas las pruebas y también más legibles.

Con los frameworks más conocidos de pruebas unitarias ya se incluyen herramientas para facilitar la extensión de las aserciones básicas, principalmente basadas en Harmcrest, que suele ayudar también a tener mensajes de error más explicativos. Pero estas a veces pueden ser un poco complejas tanto de escribir como de entender.

Para conseguir aserciones aún más claras y fluidas (fluent assertions) existe un proyecto llamado AssertJ (heredero de FEST assertions). A continuación vamos a ver un caso de una aserción usando el modelo basado en Harmcrest:

    @Test
    public void test_age() throws Exception {
        assertThat(odersky.age(), greaterThan(50));
        assertThat(odersky.age(), greaterThan(55));
        assertThat(odersky.age(), lessThan(80));
        assertThat(odersky.age(), not(0));
        assertThat(odersky.age(), greaterThan(0));
    }

Como se puede ver estas aserciones son una única por cada acción, desconexas y dan la sensación que estamos verificando cosas desconexas cuando en realidad todas están conectadas entre ellas.

A continuación vemos el mismo código usando AssertJ:

    @Test
    public void test_age() throws Exception {
        assertThat(odersky.age())
                .isGreaterThan(50)
                .isBetween(55, 80)
                .isNotZero()
                .isPositive();
    }

Como se puede ver es mucho más fluido y sencillo de entender lo que se está intentando hacer, además de que podemos ver que toda la aserción está relacionada lo cual ayuda a la regla de verificar una única cosa en cada tests para evitar efectos colaterales.

AssertJ añade cosas interesantes como soporte para Joda time o Guava collections (algunas), a continuación os dejamos un par de ejemplos:

    @Test
    public void test_birthdate() throws Exception {
        assertThat(odersky.getBirthday())
                .isBefore(DateTime.now())
                .isEqualTo("1958-09-05");
    }

    @Test
    public void contribute() throws Exception {
        Project scala = new Project("Scala");
        Developer one = Developer.builder().name("First").build();
        Developer two = Developer.builder().name("Second").build();
        scala.getContributors().put(Boolean.FALSE, one);
        scala.getContributors().put(Boolean.FALSE, two);

        Developer odersky = Developer.builder().name("Martin Ordersky").build();
        scala.getContributors().put(Boolean.TRUE, odersky);

        assertThat(scala.getContributors())
                .containsValues(one, two, odersky)
                .containsKeys(Boolean.FALSE, Boolean.TRUE)
                .contains(entry(Boolean.TRUE, odersky))
                .hasSize(3);
    }

Y por último comentar que también dispone de aserciones para Swing (aunque ellos también ponen en duda la vida que le queda a Swing) o para Neo4j. Además es muy extensible por lo que permite añadir nuevas aserciones pero también generar las aserciones semánticas a través de nuestro código, incluyendo un plugin de maven para facilitar la tarea.

Como siempre podéis ver el código en nuestro repositorio de github.

clientes Json-Rest: con menos código menos errores

Siguiendo el plan iniciado con Lombok de preservar nuestros preciados dedos y como dicen que realmente los desarrolladores brillamos por nuestro interés en evitar al máximo el escribir código, vamos a centrarnos hoy en Retrofit.

Introducción

El objetivo de retrofit es crear clientes REST para Java (primcipalmente para Android). Lo he conocido gracias a este curso de coursera sobre programación de servicios en la nube para dispositivos Android.

Como sabéis REST (y en gran parte JSON) se ha convertido en la lingua franca para la comunicación entre servicios web, cosa que es especialmente interesante en el desarrollo actual y especialmente con el desarrollo de dispositvos móviles. Esto quiere decir que vamos a tener que escribir muchos servicios web en adelante y muchos clientes para estos servicios web. Aquí es donde retrofit (producto open source de Square) nos ayudará haciendo el trabajo pesado de bajo nivel un poco más sencillo.

La idea es que vamos a crear interfaces en Java que representen nuestros servicios web, y retrofit nos va a proporcionar en tiempo de ejecución su implementación, encargandose de cosas como la serialización y deserialización de estructuras en JSON (también se pueden usar otros formatos como XML o protocol buffers).

Lo primero que tenemos que hacer es definir la interfaz que sabemos que tenemos en el servicio web, añadiendo anotaciones para definir el verbo utilizado (@GET, @POST…), o para definir parámetros en la URL (@Path, @Query), parámetros del cuerpo de la petición (@Field, @RequestBody) o incluso cabeceras (@Headers). Además de todas estas opciones existen interceptores para modificar de manera genérica las peticiones.

Después deberemos construir el adaptador, donde podemos configurar muchas cosas incluso el cliente HTTP que queremos usar (esto en Android puede ser muy relevante) o también como queremos hacer la serialización, pudiendo cambiar el serializador por defecto de JSON (usando gson) a XML o similar.

Además de todo esto también dispone de programación reactiva (Rx programming) para lo cual nuestros tipo de retorno pueden ser Observables o Callbacks, cosa muy útil en el caso la programación multihilo de Android. También dispone de gestión de errores.

Ejemplo

Para este ejemplo veremos dos tipo de peticiones con a la API de Yo.

public interface YoApi {

 public static final String BASE_URL = "https://api.justyo.co";

 @FormUrlEncoded
 @POST("/yo")
 Response yo(@Field("api_token") String token, @Field("username") String user);

 @GET("/subscribers_count")
 SubscriptionsResult subscribersCount(@Query("api_token") String token);

}

Aquí hemos definido la interfaz, y como véis tenemos en el primer caso un tipo de retorno Response que se usa en este caso porque el método POST no tiene ningún retorno. En el segundo caso si que hay una respuesta muy simple que sólo contiene el campo result. A continuación podemos ver el bean que se corresponde:

import lombok.Value;

@Value
public class SubscriptionsResult {
    private int result;
}

Finalmente para poder utilizarlo podemos verlo en el código de la clase YoApiTest:

    @BeforeClass
    public static void init() {
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(YoApi.BASE_URL)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build();
        client = restAdapter.create(YoApi.class);
    }

    @Test
    public void testSubscribersCount() throws Exception {
        SubscriptionsResult subscriptionsResult = client.subscribersCount(API_KEY);
        Assert.assertEquals(0, subscriptionsResult.getResult());
    }

    @Test
    public void testYo() throws Exception {
        Response yoResponse = client.yo(API_KEY, YO_FRIEND);

        Assert.assertEquals(200, yoResponse.getStatus());
    }

Y con esto tenemos toda la configuración y el código necesario para hacer llamadas a nuestro servicio web. Además hemos incluido un ejemplo del servicio HostIp (ejemplo de petición) donde cambiamos alguna cosa más y hay algo más de código. Si queréis podéis echar un vistazo a la interfaz y al test.

Conclusión

Sin duda es una pieza de código interesante. El código final queda muy límpio y parece ser muy configurable. También he visto que hay alguna alternativa (Volley o incluso OkHttp del propio square aunque este se puede integrar en retrofit.) pero no he tenido tiempo de evaluarla. Parece una cosa muy interesante y sin duda si quisiera hacer servicios web a nivel Android me lo plantearía muy seriamente, no sé si sería mi elección al hacer llamadas de alta concurrencia en algún tipo de servicio de servidor, pero al utilizar RxJava seguramente también sería una buena opción.

En fin, me alegra ver que startups (aunque ya no tan starting :P) se dedican a proporcionar a la comunidad este tipo de proyectos.

Alarga la vida a tus dedos con Lombok

Introduccion

Una de las cosas que más se critica a Java es su verbosidad (el conocido como boilerplate code) que provoca muchas veces dedicar mucho tiempo a escribir código repetitivo para conseguir las mismas cosas que con otros lenguajes se consigue con mucho menos código. Aunque los IDE’s ayudan, es cierto que al final del día dedicar tiempo a este tipo de tareas no es nada productivo (ni tampoco muy bueno para nuestras sufridas manos :P).

 

Pues aquí es donde aparece el proyecto Lombok. A pesar de la extraña apariencia de la web es un proyecto bastante activo y soportado (en la empresa que trabajo actualmente lo usan). Pues bien lo que hace Lombok es a través de anotaciones evita al programador tener que escribir una y otra vez el mismo código repetitivo y sin valor añadido evitando a su vez errores de tipografía o typos. Básicamente utiliza estas anotaciones para escribir por nosotros el código antes de pasar el compilador.  Los ejemplos clásicos de código repetitivo pueden ser desde los getters/setters pasando por los constructores, los métodos toString/hashCode/equals, étc. Si quieres ver una lista completa de las opciones, puedes visitar su documentación.

Sigue leyendo

Ejecutando JavaScript en Java con Nashorn embebido

(Readme in English)
En un post previo explicaba cómo embeber Rhino en una aplicación. En aquel momento no usé Nashorn porque dependía de Java 8 que aún no estaba disponible. Ahora que hace poco que tenemos la última versión de Java disponible me he decidido a explorar la nueva implementación.

Básicamente he clonado el repositorio del post anterior y lo he refactorizado para usar el nuevo motor JavaScript:

  • he borrado todas las referencias maven porque Nashorn es una biblioteca nativa de Java 8,
  • he hecho algunos cambios estéticos en nombre de paquetes para que sea consistente y
  • he refactorizado y rebautizado la clase RhinoEcmaEvaluator como NashornEcmaEvaluator

Ejecutando scripts con Nashorn

Veamos cómo ejecutaríamos un script con Nashorn:

private static EcmaValue evaluateExpression(String expression,
        List variables) {
    ScriptEngineManager scriptManager = new ScriptEngineManager();
    ScriptEngine nashornEngine = scriptManager.getEngineByName("nashorn");
 
    try {
        putJavaVariablesIntoEngine(nashornEngine, variables);
        Object javaValue = nashornEngine.eval(expression);
        EcmaValue value = EcmaValue.create(javaValue);
        return value;
    } catch (ScriptException e) {
        handleException(e, variables);
        return null;
    }
}

Así pues ejecutar un script con Nashorn consiste en dos pasos:

  • obtener una referencia al motor Nashorn (líneas 3 y 4)
  • y ejecutar el script (línea 8)

Algunas consideraciones:

  • La clase EcmaValue no es parte de Nashorn; es una clase mía que uso a forma de wrapper de los valores que entran y salen del motor JavaScript.
  • La línea 7 (que es opcional) pasa variables del contexto Java al contexto JavaScript

Pasando objetos Java a Nashorn

La manera en la que Nashorn gestiona el binding de variables entre Java y JavaScript es más sencillo que en Rhino. No hay necesidad de crear o manipular contextos JavaScript y crear wrappers encima de objetos, sino que simplemente hay que:

  • instanciar un objeto de Binding, una clase similar a un Map (línea 4),
  • añadir los objetos Java a este mapa con la key que corresponda (línea 20),
  • y finalmente enlazar la instancia de Binding en el scope JavaScript adecuado (línea 10).
private static void putJavaVariablesIntoEngine(ScriptEngine engine,
        List variables) {
 
    Bindings bindings = new SimpleBindings();
 
    for (EcmaVariable variable : variables) {
        putJavaVariableIntoEcmaScope(bindings, variable);
    }
 
    engine.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
}
 
private static void putJavaVariableIntoEcmaScope(Bindings bindings,
        EcmaVariable variable) {
 
    String variableName = variable.getName();
    EcmaValue ecmaValue = variable.getValue();
    Object javaValue = ecmaValue.getValue();
 
    bindings.put(variableName, javaValue);
}

Gestionando errores JavaScript

La manera en la que Nashorn gestiona las excepciones JavaScript es muy similar a Rhino. El método Script#eval lanza una excepción genérica (una instancia de ScriptException) que debes inspeccionar para determinar el error ECMA que ha producido el problema.

El siguiente snippet muestra cómo hacerlo.

private static final String REFERENCE_ERROR = "ReferenceError";
 
private static void putJavaVariableIntoEcmaScope(Bindings bindings,
        EcmaVariable variable) {
 
    String variableName = variable.getName();
    EcmaValue ecmaValue = variable.getValue();
    Object javaValue = ecmaValue.getValue();
 
    bindings.put(variableName, javaValue);
}
 
private static EcmaValue handleException(ScriptException exception,
        List variables) {
    if (isReferenceError(exception)) {
        throw new IllegalArgumentException("I couldn't resolve some "
            + "variable on expression with vars "
            + Arrays.toString(variables.toArray()), exception);
    }
    throw new RuntimeException(exception);
}
 
private static boolean isReferenceError(ScriptException exception) {
    String message = exception.getMessage();
    return message.startsWith(REFERENCE_ERROR);
}

Conclusiones

  • El JDK8 está en la calle junto con una implementación JavaScript totalmente nueva: Nashorn.
  • Es el nuevo motor estándar de Java (la última release de Rhino data de juno del 2012).
  • Proporcionar una API más concisa con menos código “bolierplate” que Rhino.

Nashorn proporcionar otras muchas características que aún no he podido explorar tales como un rendimiento mucho mayor, integración de consola, sencilla integración Java y JavaScript y funcionalidades que aprovechan las nuevas características funcionales de Java 8.

Ejecutando JavaScript en Java con Mozilla Rhino embebido

(Readme in English)

El reto

En un aplicación Java en el que estoy colaborando, el usuario puede extender las funcionalidades del mismo usando scripts JavaScript en algunos puntos concretos. Algunos de los usos de estos scripts incluyen:

  • definir expresiones booleanas de una complejidad arbitraria para determinar el control de flujo en el contexto de una funcionalidad de edición de workflows o,
  • acceder y manipular a datos generados por la aplicación

Estos puntos de extensión se consideran islas aisladas que no comparten contextos de ejecución entre ellos.

Para construir esta funcionalidad necesitamos:

  1. un intérprete JavaScript para ejecutar los scripts,
  2. la posiblidad de intercambiar objetos entre los ámbitos Java y JavaScript

Ya que construir un intérprete JavaScript es matar moscas a cañonazos, el paso natural es ver qué intérpretes podemos embeber en nuestra aplicación. Algunas de las opciones son las siguientes:

De hecho la decisión fue bastante fácil:

  1. Nashorn requiere necesariamente la versión 8 del JDK que todavía no está en producción.
  2. No tenemos grandes requisitos de rendimiento, la gran ventaja de V8 con respecto a sus competidores, con lo que la mayor dificultad de integración Java / C++ no compensa su uso.
  3. Entre dos versiones de la misma implementación, prefiero utilizar la más estable, así que finalmente escogí Rhino 1.7R4

En el proyecto real, escodí el motor específico detrás de una fachada abstracta para poder cambiarlo cuando fuera necesario. En este post me concentraré en cómo implementarlo con Rhino, eliminando el ruido innecesario.

La solución

Embebiendo Rhino

Para ilustra la solución, lo haremos a través de un ejemplo que hemos publicado en GitHub como un proyecto maven. Exploremos las partes más interesantes del fichero POM; básicamente sólo es necesario especificar la dependencia de Rhino.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.softwarfair.blog</groupId>
	<artifactId>RhinoEmbeddedExample</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>jar</packaging>

	../..

	<dependencies>
		<dependency>
			<groupId>org.mozilla</groupId>
			<artifactId>rhino</artifactId>
			<version>1.7R4</version>
		</dependency>
		../..
	</dependencies>
</project>

Ejecutando JavaScript

La clase RhinoEcmaEvaluator ilustra cómo usar Rhino para ejectuar JavaScript. Contiene un único método (evaluateExpression) que recibe el script a ejecutar y una lista de variables para usar en este script. Veamos cómo funciona.

public class RhinoEcmaEvaluator {
	../..

	public static EcmaValue evaluate(String expression, SymbolTable table) {
		List&lt;EcmaVariable&gt; variables = table.getVariables();
		return evaluateExpression(expression, variables);
	}

	private static EcmaValue evaluateExpression(String expression,
			List&lt;EcmaVariable&gt; variables) {

		try {
			Context context = ContextFactory.getGlobal().enterContext();
			Scriptable scope = getScope(context, variables);
			Object result = context.evaluateString(scope, expression, "", 1, null);
			return EcmaValue.create(result);
		} catch(EcmaError error) {
			return handleException(error, variables);
		} finally {
			Context.exit();
		}
	}

	private static Scriptable getScope(Context context, List&lt;EcmaVariable&gt; variables) {
		Scriptable scope = context.initStandardObjects();
		putJavaVariablesIntoEcmaScope(scope, variables);
		return scope;
	}
	../..
}

Como podéis ver, para ejcutar un script básicamente hay que hacer tres pasos:

  1. obtener el contexto inicial (línea 13)
  2. obtener un scope inicializado con los objetos JavaScript estándar (línea 25)
  3. y finalmente evaluar el script en el contexto utilzando el scope adecuado (línea 15). Esta evaluación devuelve el valor de retorno del script como un objeto Java

Algunas consideraciones:

  1. EcmaVariable y EcmaValue son clases de nuestro proyecto (no parte de Rhino) que simplemente actúan como un wrapper de los objetos Java que pueden “entrar” o “salir” del contexto de ejecución de JavaScript (estaba abstracción permitiría cambiar fácilmente la implementación del motor JavaScript)
  2. En la línea 26, introducimos objetos Java dentro del scope de Rhino. Discutiremos esto más extansamente después.
  3. La ejecución JavaScript debe anidarse en un bloque try/catch para asegurarse que todos los recursos son liberados incluso si se produce algún error imprevisto

Veamos algunos tests para enteder cómo funciona.

@Test
public void testEvaluateSimpleIntExpressionNoVariable() {
	int intValue = 3;
	String expression = "" + intValue;
	SymbolTable table = new SymbolTable();

	EcmaValue actualValue = RhinoEcmaEvaluator.evaluate(expression, table);

	assertEquals(intValue, actualValue.getValue());
}

@Test
public void testEvaluateIntExpressionNoVariable() {
	String expression = "Math.pow(2,3)";
	SymbolTable table = new SymbolTable();

	EcmaValue actualValue = RhinoEcmaEvaluator.evaluate(expression, table);

	double expectedValue = Math.pow(2, 3);
	assertEquals(expectedValue, actualValue.getValue());
}

@Test
public void testEvaluateSimpleStringExpressionNoVariable() {
	String strValue = "Hello world";
	String expression = "\"" + strValue + "\"";
	SymbolTable table = new SymbolTable();

	EcmaValue actualValue = RhinoEcmaEvaluator.evaluate(expression, table);

	assertEquals(strValue, actualValue.getValue());
}

Se trata de ejemplos básicos que ejecutan scripts muy simples:

  1. Si no se pasan variables al contexto JavaScript, entoces la SymbolTable está vacía.
  2. El EcmaValue retornado “wrapea” el valor Java real que se puede obtener mediante el método getValue

Pasando objetos Java al intérprete Rhino

Rhino proporciona una integración entre Java y JavaScript sencilla y elegante. Gracias a su funcionalidad LiveConnect, acceder a Java desde la ejecución JavaScript es casi trivial. Para añadir un objeto Java, la única cosa que tienes que hacer es poner la instancia como una propiedad en el contexto adecuado.

Veámoslo en el código:

private static void putJavaVariablesIntoEcmaScope(Scriptable scope,
		List variables) {

	for (EcmaVariable variable : variables) {
		putJavaVariableIntoEcmaScope(scope, variable);
	}
}

private static void putJavaVariableIntoEcmaScope(Scriptable scope,
		EcmaVariable variable) {

	String variableName = variable.getName();
	EcmaValue ecmaValue = variable.getValue();
	Object javaValue = ecmaValue.getValue();

	Object wrappedValue = Context.javaToJS(javaValue, scope);
	ScriptableObject.putProperty(scope, variableName, wrappedValue);
}

Como vimos, llamábamos al método putJavaVariablesIntoEcmaScope para poner nuestros objetos Java en el scope JavaScript. Veamos cómo funciona:

  1. obtenemos el nombre de la variable y el objeto Java nativo (líneas de la 12 a la 14),
  2. wrapeamos el valor (línea 16)
  3. y finalmente lo añadimos al scope (línea 17)

Veámoslo en acción con algunos tests.

@Test
public void testEvaluateStringExpressionVariable() {
	String expression = "\"how many? \" + i";
	SymbolTable table = new SymbolTable();
	table.putSymbol("i", EcmaValue.create(10));

	EcmaValue actualValue = RhinoEcmaEvaluator.evaluate(expression, table);

	String expectedValue = "how many? 10";
	assertEquals(expectedValue, actualValue.getValue());
}

@Test
public void testEvaluateStringExpressionTwoVariables() {
	String expression = "Math.pow(i,j)";
	SymbolTable table = new SymbolTable();
	table.putSymbol("i", EcmaValue.create(2));
	table.putSymbol("j", EcmaValue.create(3));

	EcmaValue actualValue = RhinoEcmaEvaluator.evaluate(expression, table);

	double expectedValue = Math.pow(2, 3);
	assertEquals(expectedValue, actualValue.getValue());
}

Gestionando los errores

¿Qué sucede si el script intenta acceder a una variabla que no existe en el context JavaScript? En nuestro caso esto podría pasar si, por ejemplo, invocamos el método evaluateExpression con un script que referencia una variable llamada “i” pero no se pasa niguna variable “i” en la SymbolTable.
Como queremos gestionar este escenario en nuestra aplicación, hemos decidido que esta situación debería ser notificada mediante el lanzamiento de una IllegalArgumentException. El siguiente test ilustra esta expectativa:

variable">@Test(expected=IllegalArgumentException.class)
public void testEvaluateVariableNotInSymbolTable() {
	String expression = "i";
	SymbolTable table = new SymbolTable();

	RhinoEcmaEvaluator.evaluate(expression, table);
}

Rhino lanza una EcmaError (que se trata de una RuntimException) siempre que sucede cualquier tipo de problema durante la ejecución JavaScript. Esta clase mimetiza el error análogo en la especificación ECMA. EcmaError no tiene subclases, asíq ue no queda más remedio que explorar el contenido de la instancia para determinar qué ha provocado el problema, en particular a través del atributo errorName. Veamos pues, cómo implementar nuestro requerimiento:

private static final String REFERENCE_ERROR = "ReferenceError";

private static EcmaValue evaluateExpression(String expression,
		List&lt;EcmaVariable&gt; variables) {

	try {
		Context context = ContextFactory.getGlobal().enterContext();
		Scriptable scope = getScope(context, variables);
		Object result = context.evaluateString(scope, expression, "", 1, null);
		return EcmaValue.create(result);
	} catch(EcmaError error) {
		return handleException(error, variables);
	} finally {
		Context.exit();
	}
}

private static EcmaValue handleException(EcmaError error,
		List&lt;EcmaVariable&gt; variables) {

	if (REFERENCE_ERROR.equals(error.getName())) {
		throw new IllegalArgumentException("I couldn't resolve some "
			+ "variable on expression with vars "
			+ Arrays.toString(variables.toArray()), error);
	}
	throw error;
}

Conclusiones

  • Si necesitamos integrar un motor JavaScript en tu aplicación Java, tienes varias opciones (¡no implementes un intérprete propio!)
  • Nashorn será la implementación nativa en el JDK8 y promete nuevas funcionalidades y un gran rendimiento.
  • Mientras tanto, Rhino es una buena opción, muy fácil de integrar y con potentes funcionalides de interoperabilidad

QuickCheck en la JVM

Como muchos otros techies a mi me encanta probar cosas nuevas y ahora mismo hemos acabado uno de los MOOC relacionado con Scala. En estos cursos se suele aprender muchas cosas, y colateralmente estoy aprendiendo una cosa que creo que puede ser de interés son las herramientas basadas en QuickCheck que son de esas cosas que nos pueden servir en un momento dado.

Os pongo en antecedentes:

El problema

Como podréis imaginar por los posts publicados en soft.war.fair creemos firmemente en la calidad del software basándose en los tests. Pero cuando te enfrentas a los tests, por ejemplo usando TDD, suele aparece una duda existencial que es: ¿cuál es el juego de prueba que tengo que usar? Y en especial, ¿Hasta donde tengo que escribir códigos de prueba? Obviamente suele haber un tradeoff a la hora de elegir nuestra batería de tests, se suele empezar por los casos base y después extenderlo con casos más complejos, pero ¿realmente estamos soportando todos los posibles casos? Seguramente no, aunque en muchas ocasiones no es importante en otras si lo es. Además para poder soportar más casos tenemos que escribir más y más código de pruebas, que al final hace que nuestra base de código crezca de manera importante.

La solución

Pues bien, en el entorno del lenguaje funcional Haskell desde hace mucho tiempo (desde el 99!) existe QuickCheck que básicamente lo que sirve para generar propiedades que representan aquello que queremos verificar y la herramienta se encarga de validarlo. Estas propiedades potencialmente pueden testear todos los posibles casos, obviamente esto puede ser infinito en tiempo según el tipo de dato pero al menos buscan diferentes distribuciones generalmente aleatorias para cubrir el mayor el mayor número de posibilidades. Otra cosa importante es que se suele simplificar el código y además se suelen probar casos extremos (esto según la herramienta) de manera que no tenemos que preocuparnos mucho más allá.

El ejemplo

Para este caso iremos a una cosa muy sencilla, simplemente tenemos un rectángulo y probamos que cualquier rectángulo válido cumple unas propiedades como por ejemplo que se genera bien el área o la hipotenusa. Con este ejemplo podemos ver la potencia directamente, pero pensad que la gracia está en poder probar con muy poco código propiedades más complejas, como la posibilidad de poder trabajar con varios rectángulos.

Este será el código que queremos testear:

public class Rectangle {
    private int height;
    private int width;
	…
    public long area(){
        return height * width;
    }

    public boolean biggerThan(Rectangle that) {
        return area() > that.area();
    }

    public void add(int n) {
        validate(height +n, width +n);
        this.height = height +n;
        this.width = width +n;
    }
}

Y aquí podéis ver como serían algunos test al estilo habitual, probando casos base y después algún caso más genérico:

    @Test
    public void testAreaEmpty1() throws Exception {
        Rectangle cut = new Rectangle(0,0);
        Assert.assertEquals(0l, cut.area());
    }

...

    @Test
    public void testAreaCorrectOneXTwo() throws Exception {
        Rectangle cut = new Rectangle(1,2);
        Assert.assertEquals(2l, cut.area());
    }

Primer candidato: jUnit-quickcheck

En este caso, como su nombre indica, sólo se puede utilizar con el framework jUnit. Se basa en anotaciones y permite ejecutar un test que recibe parámetros muchas veces con dichos parámetros diferentes siguiendo un patrón. Es algo parecido a los parametrized tests, pero bastante más potente y conciso. Los tipos de datos que se pueden usar como parámetros son principalmente tipos primitivos y algunas collections, pero se puede ampliar con tus propios tipos aunque es un poco engorroso.

También puedes configurar los parámetros para que sean con unos ciertos límites que funciona muy bien con los tipos predefinidos.

Veámos un ejemplo

@Theory
    public void testAreaFineGrained(@ForAll @InRange(minInt = 0, maxInt = 300) int x,
                                    @ForAll @InRange(minInt = 0, maxInt = 300) int y) throws Exception {
        Rectangle out = new Rectangle(x,y);
        Assert.assertEquals(x*y, out.area());
    }

    @Theory
    public void testNotIsSquare(@ForAll @InRange(minInt = 0) int x,
                                    @ForAll @InRange(minInt = 0) int y) throws Exception {
        Assume.assumeThat(x, is(not(y)));
        Rectangle out = new Rectangle(x,y);
        Assert.assertFalse(out.isSquare());

    }

En el primer caso estamos generando los parámetros entre 0 y 300, el framework por defecto hará 100 ejecuciones aunque esto se puede configurar.

Mientras en el segundo caso aparece la necesidad de asegurarse que no sean iguales, y la manera de hacerlo es con las asunciones de jUnit, pero con el riesgo de que si en ninguna de las ejecuciones lanzadas se cumple la asunción el test fallará por falta candidatos.

Segundo candidato: QuickCheck

Si, el nombre es muy original😛. En este caso es agnóstico del tipo de test que estemos haciendo y como veréis es más tedioso de construir/configurar pero algo más potente.

En este caso la idea es tener (algunos ya implementados) generadores de objetos, pudiendo componer los generadores para por ejemplo construir objetos más complejos. Aquí podemos ver un generador de Rectángulos:

class RandomRectangleGenerator implements net.java.quickcheck.Generator{
    Generator x = PrimitiveGenerators.positiveIntegers();
    Generator y = PrimitiveGenerators.positiveIntegers();

    @Override public Rectangle next() {
        return new Rectangle(x.next(), y.next());
    }
}

Después para usarlo puede ser tan simple como esto:

  @Test
    public void testArea() throws Exception {
        for (Rectangle rectangle : Iterables.toIterable(new RandomRectangleGenerator())) {
            Assert.assertEquals(rectangle.getHeight() * rectangle.getWidth(), rectangle.area());
        }
    }

Se supone que deben funcionar también las anotaciones pero creo que no funciona del todo bien.

Esta herramienta tiene cosas interesantes, como la generación aleatoria basada en un semilla fija. Y vosotros diréis, lo cualo??? Pues me refiero a que aunque se generen enteros aleatorios para poder cubrir de manera significativa el espectro de los enteros, los enteros que se van a generar serán siempre los mismos, por lo que tendremos cierta predictibilidad útil a veces para, por ejemplo, ejecutar tests en servidores de integración continua. A esto se le llama generación determinista. También tiene generación según estratégias: sólo valores únicos, basado en transformaciones, excluyendo valores, etc.

Esta herramienta es sin duda útil para poder tener nuestros generadores de objetos de manera rápida y sencilla.

Tercer y último (por ahora) candidato: ScalaCheck

En este caso, como ya he comentado antes, se trata de una librería para ser usada en Scala, claro que entonces alguno pensará que no se pueden testear clases java, pero no es así pues todo Scala se transforma a una clase Java y por tanto la interoperabilidad es casi transparente (a excepción de algunos tipos de datos).

En el caso de ScalaCheck también se basa en el concepto de generadores pero algo más avanzado y libre a la vez. Al tratarse de un lenguaje funcional está muy orientado a tener propiedades a verificar y permitir tener un modelo muy cercano al lenguaje natural. Una propiedad puede ser un tipo de verificación con una serie de datos aleatorios, con la gracia que la librería te permite “unir” propiedades por ejemplo diciendo que se tienen que validar todas o al menos una de las propiedades, etc.

Vamos a ver un ejemplo sin utilizar aún un generador nuestro:

 property("area") = forAll { (x:Int, y:Int) =>
    (x >= 0 && y >= 0) ==> {
      val rectangle = new Rectangle(math.abs(x),math.abs(y))
      rectangle.area == x*y
      }
  }

Aquí sucede como en el caso de junit en que estamos filtrando sólo aquellos que nos interesan.

Ahora vamos a ver como se construye y se usa un generador personalizado:

object RectangleGenerator {
  // generator for the Rectangle case class
  val arbRectangleGen:Gen[Rectangle] = for {
    height     width
    rect.area == rect.getHeight * rect.getWidth
  }

  property("hypotenouse of square + int == sum hypotenouses ") = forAll {
    (rect:Rectangle, side:Int) => {
      val square = new Rectangle(side, side).hypotenuse()
      val previousHypotenouse =  rect.hypotenuse()
      rect.add(side)
      square + previousHypotenouse  == rect.hypotenuse()
    }
  }

Como podéis ver el código es realmente sencillo de entender.

Conclusiones & winner

Sobre cual es el mejor, bueno está claro que cada uno tiene sus cosas buenas y sus cosas malas así que daré mis recomendaciones para cada caso:

  • junit-quickcheck: Si lo que se quiere hacer cuadra muy bien con algunos de los parámetros indicados puede ser muy útil, pero además el código puede quedar muy límpio.

  • java quickcheck: Si se necesita un generador determinista o algún tipo de generador más complejo, esta es vuestra solución.

  • ScalaCheck: Si se tienen que combinar varias propiedades o se quiere testear todo un sistema basándose en el paradigma QuickCheck seguramente es la solución más límpia y potente pero con el aliciente de que se debe escribir en Scala.

Mi conclusión es que estas herramientas están bien conocerlas, que pueden ser muy útiles aunque hay que tener cuidado pues pueden añadir una complejidad extra y pueden ocultar que test se están realizando, aunque también es cierto es que usadas sabiamente ahorran mucho código y errores. Eso si, para funcionalidades que tienen que ser probadas exhaustivamente, como interfaces públicas, creo que pueden ser la manera ideal.

Como siempre podéis echar un ojo a nuestros ejemplos en github.

Jar que contiene jars

(Readme in English)
Soy programador y me gusta el lenguaje Java. Sé que hoy día esto puede parecer un poco anacrónico, pero creo que es un lenguaje potente que tiene un gran ecosistema a su alrededor y al que le queda todavía mucha vida por delante (aunque su muerte sea anunciada periódicamente). Dicho lo cual, me siento con derecho a criticar aquello que no me gusta de la plataforma.

Una de las cosas que me resulta más molesta a la hora de distribuir aplicaciones basadas en Java es toda la parafernalia de configuración alrededor de las dependencias y de la configuración del CLASSPATH. Aunque son cosas diferentes está íntimamente relacionados.

Cuando queremos distribuir la aplicación, el procedimiento habitual suele consistir en crear un fichero jar ejecutable, es decir añadiendo al MANIFEST.MF la ruta hacia la clase que contiene el consabido método main que lanzará toda la aplicación. Así pues, la unidad de empaquetado (y distribución) de aplicaciones de escritorio suele ser este fichero jar. El principal problema es que, a diferencia de otras unidades de empaquetado del lado servidor (como wars o ears), no admite la inclusión de dependencias (otros jars) dentro del mismo. Ello implica que cuando tenemos que distribuir nuestra querida aplicación de escritorio que usa otras bibliotecas, hemos de distribuir nuestro código y esas dependencias por separado y después instruir al usuario final para que incluya todos los artefactos en el CLASSPATH. Tedioso para desarrolladores y usuarios. En parte este proceso se suele simplificar con la inclusión de scripts de arranque que modifican el entorno de manera adecuada o especifican los parámetros necesarios a la JVM, aunque, a mi parecer, no deja de ser un pequeño parche.

Solución 1: Ensamblar un nuevo jar que contiene todo el código

Una primera solución a este problema es un procedimiento de fuerza bruta (y poco elegante) que consiste en generar un nuevo fichero jar a partir de todos los jars necesarios para ejecutar la aplicación. Es decir, desempaquetamos nuestro código y el de todas nuestras dependencias y lo re-empaquetamos todo junto en un nuevo y único jar. Obviamente esta solución funciona, pero supongo que se intuye rápidamente que no es la solución que contribuya a tener el ciclo de desarrollo más eficiente y mantenible (overhead de manipulación, colisión de paquetes, pérdida de trazabilidad del origen del código, etc.). De todos modos, para un proyecto pequeño con pocas dependencias puede ser una solución suficiente. Si queremos seguir esta vía, no estamos solos, ya que existe un plugin maven que se encarga de hacer exactamente esto: generar un único jar que es la agregación de nuestro código con todas las dependencias.

He creado un proyecto maven de ejemplo que sigue este procedimiento que podéis encontrar en nuestro GitHub . Veamos las partes destacables del POM:

  • el plugin que nos interesa lo podemos ver de la línea 5 a la 25,
  • en las líneas de la 16 a la 20 especificamos cuál es la clase que contiene el main, información que será incorporada en el manifest del jar para que éste sea ejecutable,
  • y finalmente incluímos algunas dependencias que se ensamblarán en el jar destino.
../..
<build>
	<plugins>
		../..
		<plugin>
			<artifactId>maven-assembly-plugin</artifactId>
			<executions>
				<execution>
					<phase>package</phase>
					<goals>
						<goal>attached</goal>
					</goals>
				</execution>
			</executions>
			<configuration>
				<archive>
					<manifest>
						<mainClass>com.softwarfair.blog.HelloWorldAssembly</mainClass>
					</manifest>
				</archive>
				<descriptorRefs>
					<descriptorRef>jar-with-dependencies</descriptorRef>
				</descriptorRefs>
			</configuration>
		</plugin>
	</plugins>
</build>

<dependencies>
	<dependency>
		<groupId>log4j</groupId>
		<artifactId>log4j</artifactId>
		<version>1.2.17</version>
	</dependency>
</dependencies>
../..

Tras ejecutar mvn install en la carpeta target tendremos el artefacto HelloWorldAssembly-1.0.0-SNAPSHOT-jar-with-dependencies.jar que contiene las clases de nuestro código y de nuestra dependencia. Como siempre, para ejectuar el jar lo hacemos con el comando java -jar <jar-file> y comprobamos que efectivamente funciona sin necesidad de referenciar ningún jar adicional.

$ java -jar HelloWorldAssembly-1.0.0-SNAPSHOT-jar-with-dependencies.jar

0 [main] INFO com.softwarfair.blog.HelloWorldAssembly - Hello world!

Solución 2: one-JAR

Otra solución mucho más elegante sería poder incluir en el propio jar un directorio lib al estilo de los wars donde se pudieran almacenar las dependencias y que el entorno de ejecución (el classloader) entendiera esta situación y cargara de allá los recursos. Esto combinaría en una misma solución una correcta modularización de la aplicación y una mayor facilidad en la distribución y ejecución de la misma.

Esta estrategia es precisamente la que implementa el proyecto open source one-JAR. Parafraseando su página web: one-JAR permite empaquetar una aplicación Java junto con sus dependencias en un único jar ejecutable. La magia consiste en que utiliza un classloader propio que es capaz de cargar clases y recursos directamente desde el interior de un fichero jar en vez de hacerlo desde el sistema de ficheros, y todo ello sin necesidad de escribir una sola línea de código adicional para que funcione.

Para ensamblar este jar único, el proyecto provee de una task Ant y de un plugin maven. En nuestro github, podéis encontrar un repositorio que utiliza el plugin de maven para el mismo código del ejemplo anterior. Las partes importantes del POM son las siguientes:

  • las líneas 5 a la 21 configuran el plugin,
  • la línea 12 indica cuál es la clase que contiene el main de nuestra aplicación
  • la línea 15 especifica cuál es qualifier del artefacto que generará el plugin.
../..
<build>
	<plugins>
		../..
		<plugin>
			<groupId>org.dstovall</groupId>
			<artifactId>onejar-maven-plugin</artifactId>
			<version>1.4.4</version>
			<executions>
				<execution>
					<configuration>
						<mainClass>com.softwarfair.blog.HelloWorldOneJar</mainClass>
						<onejarVersion>0.97</onejarVersion>
						<attachToBuild>true</attachToBuild>
						<classifier>onejar</classifier>
					</configuration>
					<goals>
						<goal>one-jar</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>
../..

Tras ejecutar mvn install en la carpeta target se habrá generado el artefacto HelloWorldOneJar-1.0-SNAPSHOT.one-jar.jar que contiene nuestra aplicación junto con sus dependencias. Antes de explorar sus interioridades, comprobamos que la ejecución produce el resultado esperado:

$ java -jar HelloWorldOneJar-1.0-SNAPSHOT.one-jar.jar

0 [main] INFO com.softwarfair.blog.HelloWorldAssembly - Hello world!

Como somos seres curiosos, queremos entender cómo funciona one-JAR e investigamos el contenido de nuestro flamante nuevo jar:

  • en la línea 4 identificamos un jar que contiene el código de nuestra aplicación,
  • en la línea 5, dentro de la carpeta lib, vemos que se ha incorporado nuestra dependencia (si tuviéramos más, obviamente aparecerían también en este directorio),
  • en la línea 42 vemos un documento de texto que contiene la licencia open source de la herramienta,
  • finalmente el resto de clases es el código que ejecuta la magia sustituyendo el classloader clásico por el de one-JAR.
$ jar -tf HelloWorldOneJar-1.0-SNAPSHOT.one-jar.jar

META-INF/MANIFEST.MF
main/HelloWorldOneJar-1.0-SNAPSHOT.jar
lib/log4j-1.2.17.jar
com/
com/simontuffs/
com/simontuffs/onejar/
.version
OneJar.class
com/simontuffs/onejar/Boot$1.class
com/simontuffs/onejar/Boot$2.class
com/simontuffs/onejar/Boot$3.class
com/simontuffs/onejar/Boot.class
com/simontuffs/onejar/Handler$1.class
com/simontuffs/onejar/Handler.class
com/simontuffs/onejar/IProperties.class
com/simontuffs/onejar/JarClassLoader$1.class
com/simontuffs/onejar/JarClassLoader$2.class
com/simontuffs/onejar/JarClassLoader$ByteCode.class
com/simontuffs/onejar/JarClassLoader$FileURLFactory$1.class
com/simontuffs/onejar/JarClassLoader$FileURLFactory.class
com/simontuffs/onejar/JarClassLoader$IURLFactory.class
com/simontuffs/onejar/JarClassLoader$OneJarURLFactory.class
com/simontuffs/onejar/JarClassLoader.class
com/simontuffs/onejar/OneJarFile$1.class
com/simontuffs/onejar/OneJarFile$2.class
com/simontuffs/onejar/OneJarFile.class
com/simontuffs/onejar/OneJarURLConnection.class
src/
src/com/
src/com/simontuffs/
src/com/simontuffs/onejar/
src/OneJar.java
src/com/simontuffs/onejar/Boot.java
src/com/simontuffs/onejar/Handler.java
src/com/simontuffs/onejar/IProperties.java
src/com/simontuffs/onejar/JarClassLoader.java
src/com/simontuffs/onejar/OneJarFile.java
src/com/simontuffs/onejar/OneJarURLConnection.java
doc/
doc/one-jar-license.txt

Queda una última duda pendiente. La máquina virtual sabe cuál es la clase principal a ejecutar mediante una entrada en el MANIFEST.MF, así que sospechamos que éste debe contener la ruta a la clase principal del código de one-JAR que se encarga del bootstrap y, efecivamente es así si inspeccionamos el contenido del MANIFEST.MF generado:

Manifest-Version: 1.0

ImplementationVersion: 1.0-SNAPSHOT
Main-Class: com.simontuffs.onejar.Boot
One-Jar-Main-Class: com.softwarfair.blog.HelloWorldOneJar

Conclusión

  • El artefacto de distribución estándar de aplicaciones de escritorio en el mundo Java es el jar ejectuable.
  • El classloader clásico no permite la inclusión de dependencias (otros jars) dentro de un jar, lo que dificulta la distribución y la ejecución local.
  • Una alternativa es crear un jar único que es el resultado de desempaquetar nuestro código y el de nuestras dependencias y re-empaquetarlo todo de manera conjunta. Este procedimiento puede simplificarse con el uso del plugin de maven assembly.
  • Otra alternativa es utilizar el proyecto one-JAR que permite el empaquetado de jars dentro de jars sustituyendo el classloader clásico por otro más avanzado que sabe inspeccionar el contenido de los ficheros jar.