furniture
Inflatable Water Slide

Dependency Injection de HttpClient 3.x

December 28th, 2010
Para un fanático de la Dependency Injection como yo, siempre me ha costado trabajar con Apache HttpClient 3.x y la multitud de maneras que tiene la gente de inicializar los clientes. Además, como usuario asiduo de Spring Framework, es imprescindible disponer de una manera de cargar clientes HTTP desde el contexto XML y que sea sencilla de probar.

Una de las soluciones más comunes para crear y configurar los clientes es la declaración de un método init() donde se inicializan todos los parámetros, obligando que nuestra clase conozca todos los detalles y parámetros de este componente externo. Pese a que la solución funciona, no deja de ser complicada de testear, y esto es justamente lo que se quiere evitar desde el Test Driven Development (TDD). Para ello, hay que buscar una fórmula que permita "inyectar" los clientes HTTP, ya configurados en el exterior, permitiendo a la clase de destino centrarse en su objetivo.

En primer lugar, hay que conseguir la biblioteca HttpClient 3.1, usando nuestro gestor de dependencias favorito: http://mvnrepository.com/artifact/commons-httpclient/commons-httpclient/3.1. Al finalizar el proceso, deberíamos tener descargadas las siguientes dependencias:

El siguiente paso es declarar una instancia de HttpClient, sin inicializar, en la clase que necesite realizar conexiones web. Además tendremos que decidir cómo se configurará la instancia desde el exterior: por inyección en el constructor, por inyección usando un método setter o utilizando algún sistema por anotaciones, como el @Autowired de Spring Framework:

import org.apache.commons.httpclient.HttpClient;

public class HttpClient3Injected {

@Autowired // Dependency injection by annotation.
private HttpClient client = null;

/**
* Dependency injection by constructor.
*/
public HttpClient3Injected(HttpClient client) {
this.client = client;
}

[...]

/**
* Dependency injection by setter.
*/
public void setClient(HttpClient client) {
this.client = client;
}

}

Sólo con esta declaración, ya podemos realizar tests unitarios con mocks, que nos permitan simular cualquier entrada y salida del HttpClient sin tener que realizar la petición real. Por ejemplo, usando JMock 2.5.x:

import static org.junit.Assert.assertNull;

import org.apache.commons.httpclient.*;
import org.jmock.*;
import org.junit.*;
import org.jmock.lib.legacy.ClassImposteriser;

public class TestHttpClient3Injected {

private HttpClient3Injected example = null;

private Mockery mockery = new Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};

private HttpClient clientMock = null;

@Before
public void setUp() throws Exception {
clientMock = mockery.mock(HttpClient.class);

example = new HttpClient3Injected(clientMock);
}

@After
public void tearDown() throws Exception {
mockery.assertIsSatisfied();
}

@Test
public void testConnectShouldReturnNullIfHttpConnectionFails() throws Exception {
mockery.checking(new Expectations() {
{
exactly(1).of(clientMock).executeMethod(with(any(HttpMethod.class)));
will(throwException(new HttpException("Oops!")));
}
});

assertNull(example.connect());
}

}

Ya podemos definir todos los casos de test que necesitemos e implementar el método connect usando TDD.

Pero ahora queda ver cómo utilizar esta clase desde el exterior. Es tan sencillo como crear una instancia de HttpClient, con la configuración que necesitemos, y facilitarla a nuestra clase:

// Simple default HttpClient
HttpClient client = new HttpClient();
HttpClient3Injected myInstance = new HttpClient3Injected(client);

// HttpClient with a multithreaded customized connection pool
HttpConnectionManagerParams mgrParams = new HttpConnectionManagerParams();
mgrParams.setDefaultMaxConnectionsPerHost(20);
mgrParams.setMaxTotalConnections(100);
mgrParams.setSoTimeout(300000);
MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
manager.setParams(mgrParams);
HttpClient client = new HttpClient(manager);
HttpClient3Injected myInstance = new HttpClient3Injected(client);

Y lo mejor de todo, gracias a este método podemos inyectar un HttpClient totalmente configurado desde XML a nuestra clase, usando un framework de inyección de dependencias, como Spring Framework:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="instance" class="cat.edra.web.HttpClient3Injected">
<constructor-arg ref="client" />
</bean>

<bean id="client" class="org.apache.commons.httpclient.HttpClient">
<constructor-arg ref="manager" />
</bean>

<bean name="manager" class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">
<property name="params">
<bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
<property name="defaultMaxConnectionsPerHost" value="20" />
<property name="maxTotalConnections" value="100" />
<property name="soTimeout" value="300000" />
</bean>
</property>
</bean>

</beans>

Como véis, este método permite centrarse en la implementación de nuestra clase, y siempre precedida de una batería completa de tests. La inyección de dependencias nos ofrece un HttpClient completamente configurado por la clase que nos invoque.

Dependency Injection de HttpClient 3.x

December 28th, 2010
Para un fanático de la Dependency Injection como yo, siempre me ha costado trabajar con Apache HttpClient 3.x y la multitud de maneras que tiene la gente de inicializar los clientes. Además, como usuario asiduo de Spring Framework, es imprescindible disponer de una manera de cargar clientes HTTP desde el contexto XML y que sea sencilla de probar.

Una de las soluciones más comunes para crear y configurar los clientes es la declaración de un método init() donde se inicializan todos los parámetros, obligando que nuestra clase conozca todos los detalles y parámetros de este componente externo. Pese a que la solución funciona, no deja de ser complicada de testear, y esto es justamente lo que se quiere evitar desde el Test Driven Development (TDD). Para ello, hay que buscar una fórmula que permita "inyectar" los clientes HTTP, ya configurados en el exterior, permitiendo a la clase de destino centrarse en su objetivo.

En primer lugar, hay que conseguir la biblioteca HttpClient 3.1, usando nuestro gestor de dependencias favorito: http://mvnrepository.com/artifact/commons-httpclient/commons-httpclient/3.1. Al finalizar el proceso, deberíamos tener descargadas las siguientes dependencias:

El siguiente paso es declarar una instancia de HttpClient, sin inicializar, en la clase que necesite realizar conexiones web. Además tendremos que decidir cómo se configurará la instancia desde el exterior: por inyección en el constructor, por inyección usando un método setter o utilizando algún sistema por anotaciones, como el @Autowired de Spring Framework:

import org.apache.commons.httpclient.HttpClient;

public class HttpClient3Injected {

@Autowired // Dependency injection by annotation.
private HttpClient client = null;

/**
* Dependency injection by constructor.
*/
public HttpClient3Injected(HttpClient client) {
this.client = client;
}

[...]

/**
* Dependency injection by setter.
*/
public void setClient(HttpClient client) {
this.client = client;
}

}

Sólo con esta declaración, ya podemos realizar tests unitarios con mocks, que nos permitan simular cualquier entrada y salida del HttpClient sin tener que realizar la petición real. Por ejemplo, usando JMock 2.5.x:

import static org.junit.Assert.assertNull;

import org.apache.commons.httpclient.*;
import org.jmock.*;
import org.junit.*;
import org.jmock.lib.legacy.ClassImposteriser;

public class TestHttpClient3Injected {

private HttpClient3Injected example = null;

private Mockery mockery = new Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};

private HttpClient clientMock = null;

@Before
public void setUp() throws Exception {
clientMock = mockery.mock(HttpClient.class);

example = new HttpClient3Injected(clientMock);
}

@After
public void tearDown() throws Exception {
mockery.assertIsSatisfied();
}

@Test
public void testConnectShouldReturnNullIfHttpConnectionFails() throws Exception {
mockery.checking(new Expectations() {
{
exactly(1).of(clientMock).executeMethod(with(any(HttpMethod.class)));
will(throwException(new HttpException("Oops!")));
}
});

assertNull(example.connect());
}

}

Ya podemos definir todos los casos de test que necesitemos e implementar el método connect usando TDD.

Pero ahora queda ver cómo utilizar esta clase desde el exterior. Es tan sencillo como crear una instancia de HttpClient, con la configuración que necesitemos, y facilitarla a nuestra clase:

// Simple default HttpClient
HttpClient client = new HttpClient();
HttpClient3Injected myInstance = new HttpClient3Injected(client);

// HttpClient with a multithreaded customized connection pool
HttpConnectionManagerParams mgrParams = new HttpConnectionManagerParams();
mgrParams.setDefaultMaxConnectionsPerHost(20);
mgrParams.setMaxTotalConnections(100);
mgrParams.setSoTimeout(300000);
MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();
manager.setParams(mgrParams);
HttpClient client = new HttpClient(manager);
HttpClient3Injected myInstance = new HttpClient3Injected(client);

Y lo mejor de todo, gracias a este método podemos inyectar un HttpClient totalmente configurado desde XML a nuestra clase, usando un framework de inyección de dependencias, como Spring Framework:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="instance" class="cat.edra.web.HttpClient3Injected">
<constructor-arg ref="client" />
</bean>

<bean id="client" class="org.apache.commons.httpclient.HttpClient">
<constructor-arg ref="manager" />
</bean>

<bean name="manager" class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">
<property name="params">
<bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
<property name="defaultMaxConnectionsPerHost" value="20" />
<property name="maxTotalConnections" value="100" />
<property name="soTimeout" value="300000" />
</bean>
</property>
</bean>

</beans>

Como véis, este método permite centrarse en la implementación de nuestra clase, y siempre precedida de una batería completa de tests. La inyección de dependencias nos ofrece un HttpClient completamente configurado por la clase que nos invoque.

¿Está mi código bien hecho?

December 26th, 2010
En mi opinión, el código está bien hecho si puede ser entendido por un programador tan rápido como este es capaz de leerlo. Si se tiene que parar a pensar, entonces el código tal vez NO esté bien hecho. Está bien hecho si Product Owner, experto en el dominio, lo ...

Tour 2011

December 6th, 2010
Ultima actualización: 6 diciembre 2010 En mi encuentro con Enrique Comba (@ecomba) durante el coderetreat que hubo en Donosti a finales de 2010, tuve la oportunidad de escucharle un montón de ideas interesantes (gracias Enrique!).  Una de ellas fue la del "ecomba Tour", inspirado en la experiencia de Corey Haines. Hace ...

Escribiendo mejores Test II

December 2nd, 2010

Vamos a seguir con recomendaciones sobre testing, en este caso le toca el turno a mejorar la forma en que escribimos nuestros asserts. Por lo general los test se componen de 3 partes, en terminología BDD serían Given-When-Then, nos vamos a centrar por tanto en como escribir mejores Then

El gran desconocido: AssertThat

Seguro que todos los que habéis usado en alguna ocasión JUnit u otro framework xUnit estáis acostumbrados a los típicos asserEquals, assertTrue, assertNotNull y algunos más, al menos yo sólo utilizaba estos hasta que me leí el goos y descubrí las bondades de assertThat, ahora ya no uso otro :P. AssertThat funciona en conjunto con la librería hamcrest, esta librería define un conjunto de Matchers y proporciona los mecanismos para crearte los tuyos propios.

Muy bien... ¿y todo esto que me aporta que no tenga ya con los assert "normales"?, pues sirve para mejorar algunas cosas:

  • AssertThat permite construir expresiones de verificación más legibles.
  • Los mensajes de error cuando un test falla son más claros.
  • Los matchers nos permiten comprobar sólo lo que es necesario, evitan, o mejor dicho, ayudan a evitar, que comprobemos cosas de más en nuestros test.
  • La posibilidad de crearnos nuestros propios matchers permite la creación de código de verificación reutilizable de una forma limpia.

  • Dicho así suena bien ¿no?, pues ahora toca demostrar estas cosas con ejemplos.

    Utilizando AssertThat con Matchers Predefinidos

    Existen un buen número de matchers predefinidos muy útiles para trabajar con cadenas, colecciones, números, concatenar matchers y alguna cosilla más. Una primera aproximación para usar hamcrest se puede hacer simplemente sustituyendo los assert "normales" que tengáis ahora mismo por su versión con assertThat.

    Vamos a verlo con un ejemplo, tenemos que comprobar que somos capaces de pintar una cadena que contenga el dni de un usuario y su nombre, algo como esto:

    El usuario Alfredo Casado tiene el dni: 123456798P

    No quiero comprobar que la cadena es exactamente esa, me basta con saber con que en la cadena aparece el nombre del usuario y el dni del mismo. De esta forma mi test no será tan frágil y si alguien cambia el ":" por una "," o algún cambio de estilo similar mi test no se vería afectado. Con los assert de siempre esto lo podría expresar así:

    Utilizando assertThat lo podría escribir del siguiente modo:

    Personalmente esta segunda forma me resulta más legible, pero no es eso lo mejor en este caso, ahora imaginar que por algún motivo el test falla, por ejemplo el dni es incorrecto, en el primer caso el error que me daría sería simplemente un "assertion error" y me tocaría investigar un poco más que es lo que realmente esta fallando, con assertThat tendría un mensaje como este:

    Un ejemplo más, ahora con colecciones, suponer que tenemos un set de cadenas con nombres de frutas, queremos que nuestro test pase si la cesta contiene una pera o bien una manzana, con assert de toda la vida nos podría quedar algo como:

    Ahora con assertThat:

    Y igual que antes, en el primer caso en caso de error tenemos poca o nula información y en el segundo caso tendríamos un mensaje de error como este:

    Existen muchos más matchers predefinidos que podéis probar, en este tutorial de hamcrest tenéis el listado con todos los matchers y algún ejemplo de como usarlos (no es para tirar cohetes el tutorial eso si).

    Creando nuestros propios Matchers

    Con lo visto hasta ahora ya hemos ganado algo pero la verdadera potencia de assertThat esta en la posibilidad de construir tus propios matchers, vamos a ver como se hace con un ejemplo. Esta vez vamos con algo un poco más realista que las peras y las manzanas. Supongamos que tengo una clase EmailNotifier que envía correos electrónicos a una dirección determinada, quiero comprobar que la clase funciona y para esto necesito verificar que en la bandeja de entrada del usuario tengo el mensaje esperado. Para simplificar me voy a conformar con verificar que tengo un mensaje con el asunto correcto, mi test podría tener esta pinta:

    Si sin saber nada de matchers ni de hamcrest entendéis este test, entonces prueba superada!, de eso se trataba todo, de escribir test que se lean y que dejen claras sus itenciones. En clean code Robert Martin nos decía que el código debe ser legible y además pronunciable, si leemos la última linea del test (en castellano) dice : "Verifica que la bandeja de entrada de alfredo tiene un email con el asunto: asunto de test". Esta es una de las cosas que más me gustan de assertThat, que permite escribir código que se puede leer.

    Todavía rechinan cosas en este test, como por ejemplo la creación del mensaje de test, el EmailNotifier recibe un message, se trata de una interfaz así que en este caso simplemente he construido el mensaje de test con un stub (usando mockito). Se podría dejar algo mejor con un builder, pero de estos temas, mocks&stubs y builders quiero hablar más en detalle en otra entrada así que de momento lo podemos dejar así.

    Ahora que ya entendemos la intención del test, vamos a ver los detalles de como esta construido. Tenemos un par de métodos estáticos inboxOf y hasOneEmailWithSubject, el primero se encarga de crear un objeto Inbox a partir de una dirección de correo y el segundo devuelve un matcher que se encargará de comprobar si en ese Inbox existe un mensaje con el asunto indicado. Estos dos métodos los podemos meter en alguna clase con matchers y utilidades de test, por ejemplo:

    El método inboxOf simplemente crea un objeto de la clase Inbox que se encargará de representar la bandeja de entrada del usuario, más tarde comento un par de cosillas sobre esta clase.

    El método hasOneEmailWithSubject es la gracia del invento, es un método estatico que me devuelve un objeto de tipo Matcher, en este caso como el Matcher es muy sencillo he optado por hacer simplemente una clase anónima. Para hacer un matcher la forma más común es extender de TypeSafeMatcher indicando el tipo del objeto sobre el que hacemos comprobaciones, en este caso Inbox. Posteriormente sobre-escribimos dos métodos:

    • matchesSafely, este método recibe un inbox y es donde hacemos la comprobación que en este caso consiste simplemente en verificar si el inbox tiene un mensaje con el asunto indicado
    • describeTo, este método sirve para poner una descripción de lo que estábamos esperando recibir, es lo que aparecerá después de "Expected:" en el mensaje de error que compone assertThat.

    Y eso es todo lo que hay que saber para hacer un matcher, con este enfoque conseguimos dos beneficions importantes:

    • mejorar la legibilidad de los test
    • eliminar duplicación en las comprobaciones de los test, y lo que es mejor, podemos ir construyendo nuestra propia librería de matchers adaptada a nuestro proyecto, de modo que cada vez cueste menos hacer test y además estos sean más legibles.

    Ahora los mensajes de error, cuando el test falle, por ejemplo porque la cadena con el asunto no sea la esperada, tendremos un mensaje de error como este:

    En la parte expected se muestra el mensaje que hemos puesto en el método describeTo de nuestro matcher, que es lo que estábamos esperando que sucediera y no ha sucedido. En el got lo que vemos es el toString del objeto sobre el que se hace la verificación, en este caso el objeto Inbox. El toString de inbox esta sobre-escrito para que pinte todos los mensajes que tiene en la bandeja del entrada (por simplificar sólo el asunto), de esta manera cuando el test falle tendremos información del estado de la bandeja de entrada que puede ser muy útil para determinar la causa del error. Por ejemplo en este caso vemos que realmente se ha enviado un mensaje pero el asunto no es el esperado.

    Lo único que nos queda es el código de la clase Inbox que también formará parte de mis utilidades para test (o bien puede ser una clase que utilice en mi proyecto para algo, depende), esta clase no tiene nada especial, simplemente sirve para encapsular un poco la fealdad del API java mail (que madre mía...), os dejo el código:

    A esta clase le falta un mejor control de errores y algunas refactorizaciones aquí y allá (el método toString lo esta pidiendo a gritos :P). Con esto terminamos que ya esta quedando muy largo, si esta entrada sirve para convencer al menos a uno de usar assertThat en lugar de los otros assert ya me daría por satisfecho jeje.

    -->