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:
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:
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:
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:
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.
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.
Comentarios
Publicar un comentario