<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-809960945490047135</id><updated>2011-11-06T00:57:18.988+01:00</updated><category term='SOAP'/><category term='JMock 2+'/><category term='JUnit 4+'/><category term='Java 6+'/><category term='Intro'/><category term='TDD'/><category term='Android'/><category term='Best Practices'/><category term='Unit Testing'/><category term='Matchers'/><category term='Dependency Injection'/><category term='Testing'/><title type='text'>Testing a little... in Java</title><subtitle type='html'>...probando, probando...</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://testingalittle.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>9</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-1021297115755807180</id><published>2011-11-05T18:00:00.029+01:00</published><updated>2011-11-06T00:36:02.019+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit 4+'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>Robolectric (I): Unit Testing en Android</title><content type='html'>Al intentar aprender &lt;a href="http://developer.android.com/index.html"&gt;Android&lt;/a&gt; y cómo desarrollar software sobre su plataforma, lo primero que sorprende es lo complicado (y lento) que resulta hacer TDD. Por suerte, la gente de &lt;a href="http://pivotallabs.com/"&gt;Pivotal Labs&lt;/a&gt; ha creado &lt;a href="http://pivotal.github.com/robolectric/"&gt;Robolectric&lt;/a&gt;, un framework que permite realizar Unit Tests sin disponer de un emulador Android activo.&lt;br /&gt;&lt;br /&gt;En primer lugar, debes disponer de un &lt;b&gt;proyecto Android&lt;/b&gt;, generado desde &lt;a href="http://developer.android.com/resources/tutorials/hello-world.html"&gt;Eclipse&lt;/a&gt; o desde &lt;a href="http://developer.android.com/guide/developing/projects/projects-cmdline.html"&gt;Línea de Comandos&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A partir del proyecto, y usando &lt;a href="http://maven.apache.org/guides/getting-started/maven-in-five-minutes.html"&gt;Maven&lt;/a&gt;, la instalación es inmediata. Simplemente hay que crear un fichero &lt;b&gt;pom.xml&lt;/b&gt; en la raíz del proyecto, con las siguientes dependencias:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml" name="code"&gt;[... Cabecera del Proyecto ...]&lt;br /&gt;&lt;br /&gt;&amp;lt;dependencies&amp;gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;com.google.android&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;android&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;2.3.3&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;scope&amp;gt;provided&amp;lt;/scope&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;dependency&gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;com.pivotallabs&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;robolectric&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;4.8.2&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;/dependencies&amp;gt;&lt;br /&gt;&lt;br /&gt;[... Más etapas y plugins del proyecto ...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como es normal, nuestro proyecto Android debe estar estructurado siguiendo las guías de Maven, con el código de la aplicación dentro de &lt;b&gt;src/main/java&lt;/b&gt; y el código de nuestros tests en &lt;b&gt;src/test/java&lt;/b&gt;. En el caso de los recursos gráficos (strings.xml, layouts, imágenes, ...), estos deben mantenerse en la carpeta &lt;b&gt;res&lt;/b&gt;, y no en src/main/resources, respetando la disposición de contenidos de Android). Para más información sobre cómo configurar Eclipse y Maven, el siguiente artículo es tremendamente útil y completo: &lt;a href="http://unbeagleyyo.wordpress.com/2011/03/12/empezando-con-android-maven-robolectric-y-roboguice/"&gt;http://unbeagleyyo.wordpress.com/2011/03/12/empezando-con-android-maven-robolectric-y-roboguice&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Aparte de esto, sólo hay que tener en cuenta que la dependencia &lt;b&gt;android&lt;/b&gt; debe tener un scope &lt;b&gt;provided&lt;/b&gt;, y &lt;b&gt;robolectric&lt;/b&gt; lo debe tener como &lt;b&gt;test&lt;/b&gt;. Nuestros tests tendrán que ejecutarse con el Test Runner &lt;b&gt;RobolectricTestRunner&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Y ahora ya podemos comenzar a realizar TDD. En este primer ejemplo, veamos cómo probar el flujo de ejecución entre actividades:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;import static com.xtremelabs.robolectric.Robolectric.*;&lt;br /&gt;import static org.junit.Assert.*;&lt;br /&gt;&lt;br /&gt;import org.junit.Before;&lt;br /&gt;import org.junit.Test;&lt;br /&gt;import org.junit.runner.RunWith;&lt;br /&gt;&lt;br /&gt;import android.content.Intent;&lt;br /&gt;import android.widget.Button;&lt;br /&gt;&lt;br /&gt;import com.xtremelabs.robolectric.RobolectricTestRunner;&lt;br /&gt;import com.xtremelabs.robolectric.shadows.ShadowActivity;&lt;br /&gt;&lt;br /&gt;@RunWith(RobolectricTestRunner.class)&lt;br /&gt;public class HomeActivityTest {&lt;br /&gt;&lt;br /&gt;    private HomeActivity activity;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() throws Exception {&lt;br /&gt;        activity = new HomeActivity();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testButtonShouldOpenSecondActivity() {&lt;br /&gt;        activity.onCreate(null);&lt;br /&gt;&lt;br /&gt;        Button button = (Button) activity.findViewById(R.id.button);&lt;br /&gt;        clickOn(button);&lt;br /&gt;&lt;br /&gt;        ShadowActivity shadowActivity = shadowOf(activity);&lt;br /&gt;        Intent next = shadowActivity.getNextStartedActivity();&lt;br /&gt;        assertNotNull(next);&lt;br /&gt;        assertEquals(SecondActivity.class.getName(), next.getComponent().getClassName());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En este primer ejemplo se instancia una Actividad inicial (&lt;code&gt;HomeActivity&lt;/code&gt;). Al presionar el botón que contiene, comprobamos que el control lo obtiene una segunda actividad (&lt;code&gt;SecondActivity&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Para conseguir nuestro propósito, Robolectric nos ofrece una Actividad especial llamada &lt;b&gt;ShadowActivity&lt;/b&gt;, que publica una gran cantidad de métodos que normalmente no están disponibles en la clase &lt;code&gt;Activity&lt;/code&gt;. En este caso, se está usando el método &lt;code&gt;getNextStartedActivity()&lt;/code&gt; que retorna la siguiente actividad añadida a la pila de actividades del proceso.&lt;br /&gt;&lt;br /&gt;Gracias a &lt;b&gt;Robolectric&lt;/b&gt; podremos probar prácticamente cualquier situación de nuestras Actividades y Servicios de forma aislada. En los próximos posts iré mostrando las situaciones que he ido encontrando a medida que profundizaba en el desarrollo sobre Android.&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;b&gt;Nota 1&lt;/b&gt;: Si después de realizar estos pasos aparece el error &lt;i&gt;"error inflating layout/main"&lt;/i&gt; puede ser debido a que Robolectric no encuentra el API de Android o que está usando una versión incorrecta. A mi me funcionó añadir la siguiente línea en el fichero &lt;b&gt;AndroidManifest.xml&lt;/b&gt;:&lt;br /&gt;&lt;pre class="brush: xml" name="code"&gt;&amp;lt;uses-sdk android:minSdkVersion="8" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Nota 2&lt;/b&gt;: Si aparece un error &lt;code&gt;ClassNotFoundException&lt;/code&gt; de la clase &lt;b&gt;R&lt;/b&gt;, el motivo es que Maven y/o Eclipse no tienen la carpeta &lt;b&gt;gen&lt;/b&gt; incluida. Aunque hay varias soluciones potentes (como &lt;b&gt;maven-android-plugin&lt;/b&gt;), una solución temporal es añadir al fichero &lt;b&gt;pom.xml&lt;/b&gt; el &lt;b&gt;build-helper-maven-plugin&lt;/b&gt;:&lt;br /&gt;&lt;pre class="brush: xml" name="code"&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;build-helper-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;version&amp;gt;1.4&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;executions&amp;gt;&lt;br /&gt;        &amp;lt;execution&amp;gt;&lt;br /&gt;            &amp;lt;id&amp;gt;default&amp;lt;/id&amp;gt;&lt;br /&gt;            &amp;lt;phase&amp;gt;generate-sources&amp;lt;/phase&amp;gt;&lt;br /&gt;            &amp;lt;goals&amp;gt;&lt;br /&gt;                &amp;lt;goal&amp;gt;add-source&amp;lt;/goal&amp;gt;&lt;br /&gt;            &amp;lt;/goals&amp;gt;&lt;br /&gt;            &amp;lt;configuration&amp;gt;&lt;br /&gt;                &amp;lt;sources&amp;gt;&lt;br /&gt;                    &amp;lt;source&amp;gt;${basedir}/gen&amp;lt;/source&amp;gt;&lt;br /&gt;                &amp;lt;/sources&amp;gt;&lt;br /&gt;            &amp;lt;/configuration&amp;gt;&lt;br /&gt;        &amp;lt;/execution&amp;gt;&lt;br /&gt;    &amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-1021297115755807180?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2011/11/robolectric-i-unit-testing-en-android.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/1021297115755807180'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/1021297115755807180'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2011/11/robolectric-i-unit-testing-en-android.html' title='Robolectric (I): Unit Testing en Android'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-8264237469240663824</id><published>2010-12-28T23:00:00.003+01:00</published><updated>2010-12-28T23:00:01.425+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Dependency Injection'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6+'/><title type='text'>Dependency Injection de HttpClient 3.x</title><content type='html'>Para un fanático de la &lt;a title="Dependency Injection" href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;Dependency Injection&lt;/a&gt; como yo, siempre me ha costado trabajar con Apache &lt;a title="Apache HttpClient 3.x" href="http://hc.apache.org/httpclient-3.x/"&gt;HttpClient 3.x&lt;/a&gt; y la multitud de maneras que tiene la gente de inicializar los clientes. Además, como usuario asiduo de &lt;a href="http://www.springsource.org/documentation"&gt;Spring Framework&lt;/a&gt;, es imprescindible disponer de una manera de cargar clientes HTTP desde el contexto XML y que sea sencilla de probar.&lt;br /&gt;&lt;br /&gt;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 &lt;a title="Test Driven Development" href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Test Driven Development&lt;/a&gt; (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.&lt;br /&gt;&lt;br /&gt;En primer lugar, hay que conseguir la biblioteca &lt;strong&gt;HttpClient 3.1&lt;/strong&gt;, usando nuestro gestor de dependencias favorito: &lt;a title="Cómo descargar HttpClient 3.1" href="http://mvnrepository.com/artifact/commons-httpclient/commons-httpclient/3.1"&gt;http://mvnrepository.com/artifact/commons-httpclient/commons-httpclient/3.1&lt;/a&gt;. Al finalizar el proceso, deberíamos tener descargadas las siguientes dependencias:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a title="commons-httpclient-3.1.jar" href="http://mirrors.ibiblio.org/pub/mirrors/maven2/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar"&gt;Commons HttpClient 3.1&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a title="commons-codec-1.2.jar" href="http://mirrors.ibiblio.org/pub/mirrors/maven2/commons-codec/commons-codec/1.2/commons-codec-1.2.jar"&gt;Commons Codec 1.2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a title="commons-logging-1.0.4.jar" href="http://mirrors.ibiblio.org/pub/mirrors/maven2/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar"&gt;Commons Logging 1.0.4&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;El siguiente paso es declarar una instancia de HttpClient, sin inicializar, en la clase que necesite realizar conexiones web. Además tendremos que decidir &lt;strong&gt;cómo se configurará&lt;/strong&gt; la instancia desde el exterior: por inyección en el &lt;strong&gt;constructor&lt;/strong&gt;, por inyección usando un método &lt;strong&gt;setter&lt;/strong&gt; o utilizando algún sistema por &lt;strong&gt;anotaciones&lt;/strong&gt;, como el &lt;em&gt;@Autowired&lt;/em&gt; de Spring Framework:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;import org.apache.commons.httpclient.HttpClient;&lt;br /&gt;&lt;br /&gt;public class HttpClient3Injected {&lt;br /&gt;&lt;br /&gt;  @Autowired // Dependency injection by annotation.&lt;br /&gt;  private HttpClient client = null;&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Dependency injection by constructor.&lt;br /&gt;   */&lt;br /&gt;  public HttpClient3Injected(HttpClient client) {&lt;br /&gt;    this.client = client;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  [...]&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Dependency injection by setter.&lt;br /&gt;   */&lt;br /&gt;  public void setClient(HttpClient client) {&lt;br /&gt;    this.client = client;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;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 &lt;em&gt;JMock 2.5.x&lt;/em&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;import static org.junit.Assert.assertNull;&lt;br /&gt;&lt;br /&gt;import org.apache.commons.httpclient.*;&lt;br /&gt;import org.jmock.*;&lt;br /&gt;import org.junit.*;&lt;br /&gt;import org.jmock.lib.legacy.ClassImposteriser;&lt;br /&gt;&lt;br /&gt;public class TestHttpClient3Injected {&lt;br /&gt;&lt;br /&gt;  private HttpClient3Injected example = null;&lt;br /&gt;&lt;br /&gt;  private Mockery mockery = new Mockery() {&lt;br /&gt;    {&lt;br /&gt;      setImposteriser(ClassImposteriser.INSTANCE);&lt;br /&gt;    }&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  private HttpClient clientMock = null;&lt;br /&gt;&lt;br /&gt;  @Before&lt;br /&gt;  public void setUp() throws Exception {&lt;br /&gt;    clientMock = mockery.mock(HttpClient.class);&lt;br /&gt;&lt;br /&gt;    example = new HttpClient3Injected(clientMock);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @After&lt;br /&gt;  public void tearDown() throws Exception {&lt;br /&gt;    mockery.assertIsSatisfied();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Test&lt;br /&gt;  public void testConnectShouldReturnNullIfHttpConnectionFails() throws Exception {&lt;br /&gt;    mockery.checking(new Expectations() {&lt;br /&gt;      {&lt;br /&gt;        exactly(1).of(clientMock).executeMethod(with(any(HttpMethod.class)));&lt;br /&gt;        will(throwException(new HttpException("Oops!")));&lt;br /&gt;      }&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    assertNull(example.connect());&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ya podemos definir todos los casos de test que necesitemos e implementar el método &lt;em&gt;connect&lt;/em&gt; usando TDD.&lt;br /&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;// Simple default HttpClient&lt;br /&gt;HttpClient client = new HttpClient();&lt;br /&gt;HttpClient3Injected myInstance = new HttpClient3Injected(client);&lt;br /&gt;&lt;br /&gt;// HttpClient with a multithreaded customized connection pool&lt;br /&gt;HttpConnectionManagerParams mgrParams = new HttpConnectionManagerParams();&lt;br /&gt;mgrParams.setDefaultMaxConnectionsPerHost(20);&lt;br /&gt;mgrParams.setMaxTotalConnections(100);&lt;br /&gt;mgrParams.setSoTimeout(300000);&lt;br /&gt;MultiThreadedHttpConnectionManager manager = new MultiThreadedHttpConnectionManager();&lt;br /&gt;manager.setParams(mgrParams);&lt;br /&gt;HttpClient client = new HttpClient(manager);&lt;br /&gt;HttpClient3Injected myInstance = new HttpClient3Injected(client);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Y lo mejor de todo, gracias a este método &lt;strong&gt;podemos inyectar un HttpClient totalmente configurado desde XML a nuestra clase&lt;/strong&gt;, usando un framework de inyección de dependencias, como Spring Framework:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml" name="code"&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot;?&amp;gt;&lt;br /&gt;&amp;lt;beans xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot; &lt;br /&gt; xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;&lt;br /&gt; xsi:schemaLocation=&amp;quot;http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd&amp;quot;&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;bean id=&amp;quot;instance&amp;quot; class=&amp;quot;cat.edra.web.HttpClient3Injected&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;constructor-arg ref=&amp;quot;client&amp;quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;bean id=&amp;quot;client&amp;quot; class=&amp;quot;org.apache.commons.httpclient.HttpClient&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;constructor-arg ref=&amp;quot;manager&amp;quot; /&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;bean name=&amp;quot;manager&amp;quot; class=&amp;quot;org.apache.commons.httpclient.MultiThreadedHttpConnectionManager&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;property name=&amp;quot;params&amp;quot;&amp;gt;&lt;br /&gt;      &amp;lt;bean class=&amp;quot;org.apache.commons.httpclient.params.HttpConnectionManagerParams&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;defaultMaxConnectionsPerHost&amp;quot; value=&amp;quot;20&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;maxTotalConnections&amp;quot; value=&amp;quot;100&amp;quot; /&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;soTimeout&amp;quot; value=&amp;quot;300000&amp;quot; /&amp;gt;&lt;br /&gt;      &amp;lt;/bean&amp;gt;&lt;br /&gt;    &amp;lt;/property&amp;gt;&lt;br /&gt;  &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como véis, este método permite &lt;strong&gt;centrarse en la implementación de nuestra clase&lt;/strong&gt;, 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.&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-8264237469240663824?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2010/12/dependency-injection-de-httpclient-3x.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/8264237469240663824'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/8264237469240663824'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2010/12/dependency-injection-de-httpclient-3x.html' title='Dependency Injection de HttpClient 3.x'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-5334729135586536301</id><published>2010-08-13T22:30:00.002+02:00</published><updated>2010-08-14T12:30:17.776+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Best Practices'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><title type='text'>"Mocking the Time": Probando métodos que dependen de la fecha</title><content type='html'>En todos los proyectos aparecen métodos que necesitan realizar comprobaciones de fechas u horas en la lógica de negocio, ya sea consultando la hora actual del sistema, ya sea comparando rangos horarios.&lt;br /&gt;&lt;br /&gt;Si antes de implementar el método le damos un pensada a cómo probarlo, aparece una duda importante: ¿cómo puedo suplantar la fecha o la hora para que mis tests no dependan del día o la hora actuales? &lt;br /&gt;&lt;br /&gt;Pongamos un ejemplo sencillo: tenemos un método que si lo ejecutamos en un día par, tiene que llamar a un servicio externo. Una primera implementación, sin pensar cómo probarlo, nos daría un código parecido a:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;&lt;br /&gt;public class TimeService {&lt;br /&gt;&lt;br /&gt;    private IExternalService externalService = null;&lt;br /&gt;&lt;br /&gt;    public void timeMethod() {&lt;br /&gt;        Calendar cal = Calendar.getInstance();&lt;br /&gt;        if (cal.get(Calendar.DAY_OF_MONTH) % 2 == 0) {&lt;br /&gt;            externalService.externalMethod();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [...]&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Un código sencillo... &lt;b&gt;¡que resulta imposible de probar correctamente&lt;/b&gt;! Como se puede ver a continuación, si el test se ejecuta en un día par, funcionará. Pero si el test se ejecuta un día impar (situación corriente en un &lt;a href="http://confluence.public.thoughtworks.org/display/CC/CI+Feature+Matrix"&gt;sistema de integración continua&lt;/a&gt;), fallará:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;&lt;br /&gt;public class TimeServiceTest {&lt;br /&gt;    &lt;br /&gt;    [...]&lt;br /&gt;&lt;br /&gt;    // ¡¡Este test sólo funciona los días pares!!&lt;br /&gt;    @Test&lt;br /&gt;    public void testTimeServiceShouldCallExternalServiceIfDayIsEven() {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                exactly(1).of(externalServiceMock).externalMethod();&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        timeService.timeMethod();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [...]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Llegados a este punto, a uno se le pueden ocurrir soluciones del estilo: "&lt;i&gt;cambiemos la fecha del sistema&lt;/i&gt;"... pero &lt;b&gt;no son muy recomendables&lt;/b&gt;. Yo prefiero aprovechar las bondades de la &lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;inyección de dependencias&lt;/a&gt;, que nos facilitará enormemente la tarea de probar el código. &lt;br /&gt;&lt;br /&gt;Una posible solución es crear una clase &lt;b&gt;TimeResolver&lt;/b&gt; (y opcionalmente su interfaz &lt;b&gt;ITimeResolver&lt;/b&gt;) y utilizarla desde nuestros servicios para consultar la fecha actual:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;package utils;&lt;br /&gt;&lt;br /&gt;import java.util.Calendar;&lt;br /&gt;import java.util.Date;&lt;br /&gt;&lt;br /&gt;public class TimeResolver implements ITimeResolver {&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public Calendar getCurrentCalendar() {&lt;br /&gt;        return Calendar.getInstance();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public Date getCurrentDate() {&lt;br /&gt;        return new Date();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Gracias a esta ayuda, ya podemos crear un test que simule lo que sucede en cualquier combinación de fechas y horas:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;&lt;br /&gt;public class TimeServiceTest {&lt;br /&gt;&lt;br /&gt;    private TimeService timeService = null;&lt;br /&gt;&lt;br /&gt;    private Mockery mockery = new Mockery();&lt;br /&gt;    private IExternalService externalServiceMock = null;&lt;br /&gt;    private ITimeResolver timeResolverMock = null;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() throws Exception {&lt;br /&gt;        timeService = new TimeService();&lt;br /&gt;&lt;br /&gt;        externalServiceMock = mockery.mock(IExternalService.class);&lt;br /&gt;        timeResolverMock = mockery.mock(ITimeResolver.class);&lt;br /&gt;&lt;br /&gt;        timeService.setExternalService(externalServiceMock);&lt;br /&gt;        timeService.setTimeResolver(timeResolverMock);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @After&lt;br /&gt;    public void tearDown() throws Exception {&lt;br /&gt;        mockery.assertIsSatisfied();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testTimeServiceShouldCallExternalServiceIfDayIsEven() {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                Calendar cal = Calendar.getInstance();&lt;br /&gt;                cal.set(Calendar.DAY_OF_MONTH, 8); // Even Day&lt;br /&gt;                exactly(1).of(timeResolverMock).getCurrentCalendar();&lt;br /&gt;                will(returnValue(cal));&lt;br /&gt;&lt;br /&gt;                exactly(1).of(externalServiceMock).externalMethod();&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        timeService.timeMethod();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testTimeServiceShouldNotCallExternalServiceIfDayIsOdd() {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                Calendar cal = Calendar.getInstance();&lt;br /&gt;                cal.set(Calendar.DAY_OF_MONTH, 7); // Odd Day&lt;br /&gt;                exactly(1).of(timeResolverMock).getCurrentCalendar();&lt;br /&gt;                will(returnValue(cal));&lt;br /&gt;&lt;br /&gt;                // External Method will not be called&lt;br /&gt;                exactly(0).of(externalServiceMock).externalMethod();&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        timeService.timeMethod();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Este par de casos de test servirán para validar que el nuevo código se comporta como esperamos:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;&lt;br /&gt;public class TimeService {&lt;br /&gt;&lt;br /&gt;    private IExternalService externalService;&lt;br /&gt;    private ITimeResolver timeResolver;&lt;br /&gt;&lt;br /&gt;    public void timeMethod() {&lt;br /&gt;        Calendar cal = timeResolver.getCurrentCalendar();&lt;br /&gt;        if (cal.get(Calendar.DAY_OF_MONTH) % 2 == 0) {&lt;br /&gt;            externalService.externalMethod();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [...]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;¡&lt;b&gt;Conseguido&lt;/b&gt;! Ya podemos asegurar el correcto comportamiento de todos los casos que queramos. Sin duda, esta pequeña indirección me ha sido muy útil siempre que he necesitado trabajar con fechas o calendarios. Espero que os sea de utilidad.&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-5334729135586536301?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2010/08/mocking-time-probando-metodos-que.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/5334729135586536301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/5334729135586536301'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2010/08/mocking-time-probando-metodos-que.html' title='&quot;Mocking the Time&quot;: Probando métodos que dependen de la fecha'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-1044270691998815954</id><published>2010-07-18T12:15:00.000+02:00</published><updated>2010-07-18T12:15:00.868+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit 4+'/><category scheme='http://www.blogger.com/atom/ns#' term='JMock 2+'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6+'/><title type='text'>Unit Testing &amp; jMock: expectations comunes</title><content type='html'>Llega un momento en que algunos de nuestros juegos de prueba empiezan a tener muchas &lt;a href="http://www.jmock.org/expectations.html"&gt;expectations&lt;/a&gt; y se empieza a complicar la implementación de nuevos tests o el mantenimiento de los existentes.&lt;br /&gt;&lt;br /&gt;Una de las consecuencias de aplicar &lt;b&gt;TDD&lt;/b&gt; es que cuánto mayor sea la complejidad del test, mayor será la complejidad de nuestro código, y por lo tanto, de su mantenimiento. Así que si empezamos a tener tests con demasiadas &lt;i&gt;expectations&lt;/i&gt;, es buen momento para plantearse el &lt;a href="http://en.wikipedia.org/wiki/Code_refactoring"&gt;refactoring&lt;/a&gt; de alguna de las clases, para simplificar o dividir su funcionalidad.&lt;br /&gt;&lt;br /&gt;Si aún así seguimos mantiendo unas expectations muy largas, y que además se comparten entre varios tests, es muy recomendable agruparlas. Gracias a la implementación de &lt;a href="http://www.jmock.org/"&gt;jMock&lt;/a&gt;, podemos declarar varios bloques de expectations en un mismo test, de tal modo que la prueba deberá cumplir las condiciones de todos sus bloques:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;@Test&lt;br /&gt;public void testFindHouseShouldReturnOneHouse() throws Exception {&lt;br /&gt;    mockery.checking(new Expectations() {&lt;br /&gt;        {&lt;br /&gt;            // Expectations #1&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    mockery.checking(new Expectations() {&lt;br /&gt;        {&lt;br /&gt;            // Expectations #2&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    // ... code of the test ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Gracias a esta característica de &lt;b&gt;jMock&lt;/b&gt;, podremos &lt;b&gt;refactorizar&lt;/b&gt; y extraer el código común en varios métodos &lt;b&gt;private&lt;/b&gt;, dentro de nuestra clase de test. Cada uno de estos métodos contendrá uno (o varios) de los bloques de &lt;i&gt;expectations&lt;/i&gt; compartidas. Por ejemplo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;@Test&lt;br /&gt;public void testFindHousesShouldReturnHousesThatMatchColor() throws Exception {&lt;br /&gt;&lt;br /&gt;    declareCommonExpectations(); // Expectations comunes&lt;br /&gt;&lt;br /&gt;    mockery.checking(new Expectations() {&lt;br /&gt;        {&lt;br /&gt;            // Expectations of this test&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    // ... code of the test ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Test&lt;br /&gt;public void testFindHousesShouldReturnHousesThatMatchLocation() throws Exception {&lt;br /&gt;&lt;br /&gt;    declareCommonExpectations(); // Expectations comunes&lt;br /&gt;&lt;br /&gt;    mockery.checking(new Expectations() {&lt;br /&gt;        {&lt;br /&gt;            // Expectations of this test&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;&lt;br /&gt;    // ... code of the test ...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...]&lt;br /&gt;&lt;br /&gt;private void declareCommonExpectations() throws Exception {&lt;br /&gt;&lt;br /&gt;    mockery.checking(new Expectations() {&lt;br /&gt;        {&lt;br /&gt;            // Este bloque contiene todas las expectations&lt;br /&gt;            // comunes que compartirán ambos tests.&lt;br /&gt;        }&lt;br /&gt;    });&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Este tipo de refactor para agrupar código común nos permitirá reducir bastante la longitud de nuestras clases de test, permitiendo que nos centremos en lo realmente importante: cuál es la entrada esperada, y cuál debe ser la salida. Sin duda, resulta especialmente útil cuando nuestra &lt;i&gt;test suite&lt;/i&gt; está repleta de métodos findX() o updateY(), cuyo resultado queremos que sea el mismo en varias de las pruebas.&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-1044270691998815954?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2010/07/unit-testing-jmock-expectations-comunes.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/1044270691998815954'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/1044270691998815954'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2010/07/unit-testing-jmock-expectations-comunes.html' title='Unit Testing &amp; jMock: expectations comunes'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-8204206617339688327</id><published>2010-04-05T13:00:00.007+02:00</published><updated>2010-07-18T12:30:41.268+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Matchers'/><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit 4+'/><category scheme='http://www.blogger.com/atom/ns#' term='JMock 2+'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6+'/><title type='text'>Matchers y jMock</title><content type='html'>A medida que la complejidad de nuestro código aumenta, también tiende a aumentar la complejidad de los tests. Al principio nos va bien con asegurarnos que la salida del método es la esperada. Pero llegado el momento en que necesitamos comprobar qué valores contiene cada campo, o qué información se utiliza para llamar a las otras clases, es hora de empezar a utilizar &lt;a href="http://www.jmock.org/matchers.html"&gt;Matchers&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Los &lt;b&gt;Matchers&lt;/b&gt; nos permiten especificar las restricciones o condiciones que deben cumplir los parámetros de entrada. En anteriores posts, cada vez que escribíamos &lt;b&gt;with(any(String.class))&lt;/b&gt;, estábamos utilizando un Matcher (uno muy genérico, que permite cualquier cadena como parámetro de entrada).&lt;br /&gt;&lt;br /&gt;Imaginemos que queremos asegurarnos que nuestro método &lt;b&gt;getWeatherForecast()&lt;/b&gt; invoca al método &lt;b&gt;getWeather()&lt;/b&gt; con una instancia de país, cuyo nombre debe ser el mismo que el recibido. Para ello vamos a utilizar el método &lt;b&gt;hasProperty()&lt;/b&gt; de la clase &lt;a href="http://www.jmock.org/javadoc/2.5.1/org/hamcrest/Matchers.html"&gt;org.hamcrest.Matchers&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;&lt;br /&gt;import static org.hamcrest.Matchers.*;&lt;br /&gt;import static org.junit.Assert.*;&lt;br /&gt;&lt;br /&gt;import org.hamcrest.Matcher;&lt;br /&gt;import org.jmock.*;&lt;br /&gt;import org.junit.*;&lt;br /&gt;&lt;br /&gt;// Omito el resto de imports internos&lt;br /&gt;&lt;br /&gt;public class BombServiceWithMatchersTest {&lt;br /&gt;&lt;br /&gt;    // Omito la declaración de dependencias/mocks y los métodos setUp() y tearDown()&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testGetWeatherShouldUseTheSameCountry() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                Matcher&amp;lt;Country&amp;gt; matcher = hasProperty("name", equalTo("Avalon"));&lt;br /&gt;&lt;br /&gt;                exactly(1).of(bombSoapClientMock).getWeather(with(matcher));&lt;br /&gt;                will(returnValue(1));&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        int result = bombService.getWeatherForecast("Avalon");&lt;br /&gt;        assertEquals(1, result);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ... other tests&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ahora tenemos un test que se asegura que nuestro servicio enviará siempre el mismo parámetro y, si en algún momento lo modificamos por error, el test nos avisará. &lt;br /&gt;&lt;br /&gt;Como véis, es muy sencillo definir restricciones a los parámetros que utilizamos. Simplemente hay que tener en cuenta algunos detalles, que dependen del Matcher utilizado. Por ejemplo, el método &lt;b&gt;hasProperty()&lt;/b&gt; requiere que la clase que estás evaluando (en mi caso, &lt;i&gt;Country&lt;/i&gt;) cumpla las convenciones de Sun sobre getters/setters. Si en la clase &lt;i&gt;Country&lt;/i&gt; no existieran los métodos &lt;b&gt;getName()&lt;/b&gt; y &lt;b&gt;setName()&lt;/b&gt;, el test no habría funcionado (y &lt;b&gt;&amp;iexcl;cuidado!&lt;/b&gt;, porque este tipo de errores sólo informan que el método no se ha invocado, y no el porqué).&lt;br /&gt;&lt;br /&gt;Para completar la explicación del ejemplo, hay que fijarse que el segundo parámetro del método &lt;b&gt;hasProperty()&lt;/b&gt; es, a su vez, un &lt;b&gt;Matcher&lt;/b&gt;. Allí podríamos definir cualquier restricción que afectara al valor del campo (en este caso forzamos que sea el valor conocido).&lt;br /&gt;&lt;br /&gt;Con los &lt;b&gt;Matchers&lt;/b&gt; se pueden conseguir unos tests muy completos, y que nos permitan detectar fácilmente si algún cambio en nuestro código rompe con el comportamiento esperado. Os recomiendo mirar todos los métodos disponibles de la clase &lt;a href="http://www.jmock.org/javadoc/2.5.1/org/hamcrest/Matchers.html"&gt;org.hamcrest.Matchers&lt;/a&gt;, entre los que destacan:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;hasEntry()&lt;/b&gt;: muy útil al trabajar con Maps, ya que nos permite definir restricciones sobre claves y valores.&lt;/li&gt;&lt;li&gt;&lt;b&gt;hasKey()&lt;/b&gt;: comprueba si un Map contiene la clave facilitada.&lt;/li&gt;&lt;li&gt;&lt;b&gt;hasItem()&lt;/b&gt;: comprueba si el elemento existe dentro de una List.&lt;/li&gt;&lt;li&gt;&lt;b&gt;startsWith()&lt;/b&gt;: muy interesante al trabajar con Strings.&lt;/li&gt;&lt;li&gt;...y muchos más que nos pueden ayudar en cualquier momento...&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Por último, para realizar más de una comprobación por parámetro disponemos del método &lt;b&gt;allOf()&lt;/b&gt;. Por ejemplo: &lt;br /&gt;&lt;code&gt;allOf(hasKey("key1"), hasKey("key2"));&lt;/code&gt;. &lt;br /&gt;&lt;br /&gt;Sin embargo, como la clase Matcher utiliza &lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html"&gt;Generics&lt;/a&gt; de forma intensa, os puede dar muchos problemas de compilación, intentando encajar los tipos de datos. Por ejemplo, el siguiente código no compila, ya que se espera un Matcher&amp;lt;Object&amp;gt; en vez de un Matcher&amp;lt;Country&amp;gt; (dichosas reglas de los &lt;i&gt;Generics&lt;/i&gt;, que van buscando el &lt;i&gt;"mínimo común múltiplo"&lt;/i&gt;):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;Matcher&amp;lt;Country&amp;gt; matcher = allOf(&lt;br /&gt;        hasProperty("name", equalTo("Avalon")),&lt;br /&gt;        hasProperty("id", equalTo("1")));&lt;br /&gt;exactly(1).of(bombSoapClientMock).getWeather(with(matcher));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;La única solución que he encontrado es definir cada Matcher por separado, para que los tipos de cada variable se vayan acomodando por pasos, en vez de intentar inferirlos de golpe:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;Matcher&amp;lt;Country&amp;gt; m1 = hasProperty("name", equalTo("Avalon"));&lt;br /&gt;Matcher&amp;lt;Country&amp;gt; m2 = hasProperty("id", equalTo("1"));&lt;br /&gt;Matcher&amp;lt;Country&amp;gt; matcher = allOf(m1, m2);&lt;br /&gt;exactly(1).of(bombSoapClientMock).getWeather(with(matcher));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Espero que los &lt;b&gt;Matchers&lt;/b&gt; os sirvan de ayuda para definir tests mucho más específicos.&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-8204206617339688327?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2010/04/matchers-y-jmock.html#comment-form' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/8204206617339688327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/8204206617339688327'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2010/04/matchers-y-jmock.html' title='Matchers y jMock'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-2081684583153302362</id><published>2009-11-22T21:00:00.006+01:00</published><updated>2010-07-18T14:20:32.580+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='SOAP'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit 4+'/><category scheme='http://www.blogger.com/atom/ns#' term='JMock 2+'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6+'/><title type='text'>Unit-Testing SOAP: ¿Qué hacemos con los Holder?</title><content type='html'>En los últimos meses he tenido que integrarme con algunos Servicios Web (&lt;a href="http://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt;). Suerte que frameworks como &lt;a href="http://cxf.apache.org/"&gt;CXF&lt;/a&gt; consiguen que la tarea sea algo más sencilla de lo que era hace unos años...&lt;br /&gt;&lt;br /&gt;El problema vino cuando intenté probar uno de mis métodos: las clases que necesitaba llamar (auto-generadas con CXF) tenían como parámetros instancias de &lt;a href="http://www.j2ee.me/javase/6/docs/api/javax/xml/ws/Holder.html"&gt;Holder&lt;/a&gt; (parámetro de entrada/salida). Y yo necesitaba utilizar los resultados de los Holder para continuar...&lt;br /&gt;&lt;br /&gt;Veamos un ejemplo. Mi código tiene que llamar a esta interfaz:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;package external.soap;&lt;br /&gt;&lt;br /&gt;import javax.xml.ws.Holder;&lt;br /&gt;&lt;br /&gt;// Omito todas las anotaciones y demás meta-información...&lt;br /&gt;public interface IWebService {&lt;br /&gt;    public String execute(int param, Holder&amp;lt;String&amp;gt; innerResult);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;E imaginemos que tiene que hacer algo tan sencillo como concatenar la salida del método, con el resultado contenido en innerResult. El test que lo comprobara podría ser tan sencillo como:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;import org.jmock.*;&lt;br /&gt;import org.junit.*;&lt;br /&gt;import static org.junit.Assert.*;&lt;br /&gt;&lt;br /&gt;import javax.xml.ws.Holder;&lt;br /&gt;import external.soap.IWebService;&lt;br /&gt;    &lt;br /&gt;public class MyServiceTest {&lt;br /&gt;    &lt;br /&gt;    private MyService myService = null;&lt;br /&gt;&lt;br /&gt;    private Mockery mockery = new Mockery();&lt;br /&gt;    private IWebService webServiceMock = null;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        myService = new MyService();&lt;br /&gt;&lt;br /&gt;        webServiceMock = mockery.mock(IWebService.class);&lt;br /&gt;        myService.setWebService(webServiceMock);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @After&lt;br /&gt;    public void tearDown() {&lt;br /&gt;        mockery.assertIsSatisfied();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testBothResultsAreConcatenated() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;          {&lt;br /&gt;            exactly(1).of(webServiceMock).execute(with(any(Integer.class)),&lt;br /&gt;                    with(any(Holder.class)));&lt;br /&gt;            will(returnValue("outer"));&lt;br /&gt;          }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        String result = myService.executeOperation(1);&lt;br /&gt;        assertEquals("outer-inner", result);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Por muchos "&lt;i&gt;inventos&lt;/i&gt;" que hagamos en el método &lt;b&gt;executeOperation()&lt;/b&gt;, no habrá manera de hacer funcionar ningún test que tenga &lt;i&gt;Holders&lt;/i&gt;, ya que del mismo modo que especificamos que el método &lt;b&gt;IWebService.execute()&lt;/b&gt; tiene que retornar la respuesta "&lt;i&gt;outer&lt;/i&gt;", &lt;b&gt;no estamos declarando en ningún sitio&lt;/b&gt; que además debería rellenar el Holder con la respuesta "&lt;i&gt;inner&lt;/i&gt;".&lt;br /&gt;&lt;br /&gt;¿Se puede hacer algo al respecto? Aunque el &lt;a href="http://www.jmock.org/scripting.html"&gt;capítulo 15 del JMock Cookbook&lt;/a&gt; explique la manera de realizar acciones concretas dentro de los objetos mock utilizando &lt;a href="http://www.beanshell.org/"&gt;BeanShell&lt;/a&gt;, creo que es una solución demasiado compleja. Sin embargo, sí que es posible encontrar una solución sencilla y fácil de implementar para estas situaciones: &lt;b&gt;extender las Action&lt;/b&gt;. Utilizando la propia jerarquía de clases de JMock, se puede redefinir el comportamiento de un objeto, implementando la clase &lt;a href="http://www.jmock.org/javadoc/2.5.1/org/jmock/api/Action.html"&gt;Action&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A la hora de la verdad, yo prefiero utilizar la subclase &lt;a href="http://www.jmock.org/javadoc/2.5.1/org/jmock/lib/action/VoidAction.html"&gt;VoidAction&lt;/a&gt;, creando una &lt;a href="http://wiki.answers.com/Q/What_is_anonymous_class_in_java"&gt;clase anónima&lt;/a&gt; y redefiniendo su método &lt;a href="http://www.jmock.org/javadoc/2.5.1/org/jmock/lib/action/VoidAction.html#invoke%28org.jmock.api.Invocation%29"&gt;invoke&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;import org.jmock.*;&lt;br /&gt;import org.junit.*;&lt;br /&gt;import static org.junit.Assert.*;&lt;br /&gt;&lt;br /&gt;import javax.xml.ws.Holder;&lt;br /&gt;import external.soap.IWebService;&lt;br /&gt;    &lt;br /&gt;public class MyServiceTest {&lt;br /&gt;    &lt;br /&gt;    private MyService myService = null;&lt;br /&gt;&lt;br /&gt;    private Mockery mockery = new Mockery();&lt;br /&gt;    private IWebService webServiceMock = null;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        myService = new MyService();&lt;br /&gt;&lt;br /&gt;        webServiceMock = mockery.mock(IWebService.class);&lt;br /&gt;        myService.setWebService(webServiceMock);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @After&lt;br /&gt;    public void tearDown() {&lt;br /&gt;        mockery.assertIsSatisfied();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testBothResultsAreConcatenated() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;          {&lt;br /&gt;            exactly(1).of(webServiceMock).execute(with(any(Integer.class)),&lt;br /&gt;                    with(any(Holder.class)));&lt;br /&gt;            will(new VoidAction() {&lt;br /&gt;                @Override&lt;br /&gt;                public Object invoke(Invocation invocation) throws Throwable {&lt;br /&gt;                    Holder&amp;lt;String&amp;gt; holder = (Holder) invocation.getParameter(1);&lt;br /&gt;                    holder.value = "inner";&lt;br /&gt;                    return "outer";&lt;br /&gt;                }&lt;br /&gt;            });&lt;br /&gt;          }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        String result = myService.executeOperation(1);&lt;br /&gt;        assertEquals("outer-inner", result);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como se puede ver, aunque la sintaxis es algo extraña, se consigue definir el comportamiento exacto de cada uno de los parámetros de entrada/salida. Sólo &lt;b&gt;hay que tener en cuenta&lt;/b&gt; que el primer parámetro es &lt;b&gt;invocation.getParameter(0)&lt;/b&gt;, el segundo es &lt;b&gt;invocation.getParameter(1)&lt;/b&gt;, y así sucesivamente.&lt;br /&gt;&lt;br /&gt;Por último, si vemos que tenemos que probar muchas veces el mismo comportamiento, y se nos repite en varios tests, siempre podemos optar por crear una &lt;a href="http://en.wikipedia.org/wiki/Inner_class"&gt;inner class&lt;/a&gt; dentro de nuestra clase de pruebas, creando una nueva instancia desde cada uno de los tests:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;    [...] &lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testBothResultsAreConcatenated() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;          {&lt;br /&gt;            exactly(1).of(webServiceMock).execute(with(any(Integer.class)),&lt;br /&gt;                    with(any(Holder.class)));&lt;br /&gt;            will(new OuterInnerAction("outer", "inner"));&lt;br /&gt;          }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        String result = myService.executeOperation(1);&lt;br /&gt;        assertEquals("outer-inner", result);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // ... other tests.&lt;br /&gt;&lt;br /&gt;    private class OuterInnerAction extends VoidAction {&lt;br /&gt;        private String returnValue = null;&lt;br /&gt;        private String holderValue = null;&lt;br /&gt;&lt;br /&gt;        public OuterInnerAction(String returnValue, String holderValue) {&lt;br /&gt;            this.returnValue = returnValue;&lt;br /&gt;            this.holderValue = holderValue;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        @Override&lt;br /&gt;        public Object invoke(Invocation invocation) throws Throwable {&lt;br /&gt;            HolderHolder&amp;lt;String&amp;gt; holder = (Holder) invocation.getParameter(1);&lt;br /&gt;            holder.value = holderValue;&lt;br /&gt;            return returnValue;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Aunque la solución no sea tan elegante como utilizar BeanShell, creo que es una manera rápida y sencilla de dotar a cada test de un comportamiento "a medida".&lt;br /&gt;&lt;br /&gt;¡Suerte!&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-2081684583153302362?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2009/11/unit-testing-soap-que-hacemos-con-los.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/2081684583153302362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/2081684583153302362'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2009/11/unit-testing-soap-que-hacemos-con-los.html' title='Unit-Testing SOAP: ¿Qué hacemos con los Holder?'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-5414205827842913723</id><published>2009-10-17T15:00:00.026+02:00</published><updated>2010-07-18T17:55:22.963+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit 4+'/><category scheme='http://www.blogger.com/atom/ns#' term='JMock 2+'/><category scheme='http://www.blogger.com/atom/ns#' term='Best Practices'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6+'/><title type='text'>Unit Tests (II): clases sin interfaz</title><content type='html'>Sigamos con los tests unitarios: ¿qué ocurre si alguna de mis dependencias no tiene interfaz? O simplemente, ¿qué ocurre si no me gusta añadir una interfaz a cada una de mis clases?&lt;br /&gt;&lt;br /&gt;Aunque recomiendo encarecidamente el uso de interfaces (ayudan a definir y delimitar el alcance de una clase), no es del todo extraño que algunas de nuestras dependencias no las tengan. Como ejemplo, veamos una que seguro que a muchos nos ha tocado implementar: un cliente para leer los mensajes de &lt;a href="http://twitter.com/"&gt;Twitter&lt;/a&gt;. Si decidimos usar el proyecto &lt;a href="http://yusuke.homeip.net/twitter4j/en/index.html"&gt;Twitter4J&lt;/a&gt;, nos encontramos con que su cliente &lt;a href="http://yusuke.homeip.net/twitter4j/en/javadoc/twitter4j/Twitter.html"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;twitter4j.Twitter&lt;/span&gt;&lt;/a&gt; no tiene interfaz.&lt;br /&gt;&lt;br /&gt;Si intentáramos hacer un test usando la guía del &lt;a href="http://testingalittle.blogspot.com/2009/10/unit-tests-i-mocking-la-interaccion.html"&gt;post anterior&lt;/a&gt;:&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;    [...]&lt;br /&gt;    private TwitterService twitterService = null;&lt;br /&gt;&lt;br /&gt;    private Mockery mockery = new Mockery();&lt;br /&gt;    private Twitter twitterMock = null;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        twitterService = new TwitterService();&lt;br /&gt;&lt;br /&gt;        twitterMock = mockery.mock(Twitter.class);&lt;br /&gt;        twitterService.setTwitter(twitterMock);&lt;br /&gt;    }&lt;br /&gt;    [...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;nos lanzaría la siguiente excepción:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;java.lang.IllegalArgumentException: twitter4j.Twitter is not an interface&lt;br /&gt;    at java.lang.reflect.Proxy.getProxyClass(Proxy.java:362)&lt;br /&gt;    at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:581)&lt;br /&gt;    at org.jmock.lib.JavaReflectionImposteriser.imposterise(JavaReflectionImposteriser.java:31)&lt;br /&gt;    at org.jmock.Mockery.mock(Mockery.java:139)&lt;br /&gt;    at org.jmock.Mockery.mock(Mockery.java:120)&lt;br /&gt;    [...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Por suerte, JMock ha pensado en nosotros (&lt;a href="http://www.jmock.org/mocking-classes.html"&gt;JMock Cookbook, capítulo 18&lt;/a&gt;) y nos permite definir &lt;i&gt;Expectations&lt;/i&gt; sobre una clase o clase abstracta, dejando a un lado la API estándar de Reflection de Java y modificando/reescribiendo el bytecode en tiempo de ejecución. Entonces, ¿qué hay que hacer para conseguir hacer mock de una clase sin interfaz?&lt;br /&gt;&lt;br /&gt;En primer lugar, hay que añadir las siguientes bibliotecas a nuestro classpath:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/org/jmock/jmock-legacy/2.5.1/jmock-legacy-2.5.1.jar"&gt;jmock-legacy&lt;/a&gt; (la misma versión que JMock. En este caso la &lt;b&gt;2.5.1&lt;/b&gt; aunque funciona con toda la rama 2.x).&lt;/li&gt;&lt;li&gt;&lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/cglib/cglib-nodep/2.2/cglib-nodep-2.2.jar"&gt;cglib-nodep&lt;/a&gt; (probado con la versión &lt;b&gt;2.2&lt;/b&gt;, funciona con todas las 2.x).&lt;/li&gt;&lt;li&gt;&lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/org/objenesis/objenesis/1.2/objenesis-1.2.jar"&gt;objenesis&lt;/a&gt; (probado con la versión &lt;b&gt;1.2&lt;/b&gt;, funciona con todas las 1.x).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Y en segundo lugar, modificar la creación del objeto Mockery del siguiente modo:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;[...]&lt;br /&gt;import org.jmock.Mockery;&lt;br /&gt;import org.jmock.Expectations;&lt;br /&gt;import org.jmock.lib.legacy.ClassImposteriser;&lt;br /&gt;[...]&lt;br /&gt;&lt;br /&gt;public class TwitterServiceTest {&lt;br /&gt;&lt;br /&gt;    private TwitterService twitterService = null;&lt;br /&gt;&lt;br /&gt;    private Mockery mockery = new Mockery() {&lt;br /&gt;        {&lt;br /&gt;            setImposteriser(ClassImposteriser.INSTANCE);&lt;br /&gt;        }&lt;br /&gt;    };&lt;br /&gt;&lt;br /&gt;    private Twitter twitterMock = null;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        twitterService = new TwitterService();&lt;br /&gt;&lt;br /&gt;        twitterMock = mockery.mock(Twitter.class);&lt;br /&gt;        twitterService.setTwitter(twitterMock);&lt;br /&gt;    }&lt;br /&gt;    [...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Como podéis ver, sólo cambia el modo de instanciar el objeto &lt;b&gt;Mockery&lt;/b&gt;. Para inicializar los &lt;i&gt;mock-objects&lt;/i&gt; de nuestras dependencias, se sigue utilizando el mismo método: &lt;code style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;context.mock(twitter4j.Twitter.class);&lt;/code&gt;. Del mismo modo la definición de Expectations de cada test sigue funcionando igual (recibir un mensaje, una excepción, enviar una actualización, ...):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;    @Test&lt;br /&gt;    public void testGetMessages() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                List&amp;lt;Object&amp;gt; statuses = new ArrayList&amp;lt;Object&amp;gt;();&lt;br /&gt;                for (int i = 0; i &lt; 10; i++) {&lt;br /&gt;                    statuses.add(new Object());&lt;br /&gt;                }&lt;br /&gt;                exactly(1).of(twitterMock).getUserTimeline();&lt;br /&gt;                will(returnValue(statuses));&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        List&amp;lt;Status&amp;gt; result = twitterService.getMessages();&lt;br /&gt;        assertNotNull(result);&lt;br /&gt;        assertEquals(10, result.size());&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;Justamente esta clase Twitter forma parte de las dependencias difíciles de integrar en nuestros tests. Parece que intenten ir en contra de cada uno de los consejos para conseguir código &lt;b&gt;&lt;i&gt;testable&lt;/i&gt;&lt;/b&gt; (&lt;a href="http://misko.hevery.com/code-reviewers-guide/"&gt;http://misko.hevery.com/code-reviewers-guide/&lt;/a&gt;). Por ejemplo, el hecho de añadir lógica y excepciones en sus constructores nos hace muy complicado poder crear un objeto &lt;a href="http://yusuke.homeip.net/twitter4j/en/javadoc/twitter4j/Status.html"&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;twitter4j.Status&lt;/span&gt;&lt;/a&gt;, ya que necesita una cadena JSON bastante complicada...De todos modos, como estamos probando una clase nuestra que tiene la dependencia con el cliente Twitter, nos interesa ver cómo se comporta ante cada una de las situaciones esperadas (excepciones de conexión, de autorización, resultados nulos, etc). Dependiendo de lo queramos implementar, sólo un pequeño número de tests necesitarán probar respuestas concretas (p.ej., qué ocurre cuando se recibe una fecha incorrecta o un campo vacío).Si queremos probar casos concretos de respuesta, siempre se puede crear una cadena JSON completa, de ejemplo, y utilizarla para crear las distintas instancias de &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;twitter4j.Status&lt;/span&gt; que necesitemos dentro de las Expectations. Con estos resultados "simulados" ya podremos ver cómo se comporta nuestra implementación en (casi) todas las situaciones posibles.&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-5414205827842913723?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2009/10/unit-tests-ii-clases-sin-interfaz.html#comment-form' title='0 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/5414205827842913723'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/5414205827842913723'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2009/10/unit-tests-ii-clases-sin-interfaz.html' title='Unit Tests (II): clases sin interfaz'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-6237364645929369645</id><published>2009-10-07T00:30:00.009+02:00</published><updated>2010-07-18T17:54:21.517+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unit Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit 4+'/><category scheme='http://www.blogger.com/atom/ns#' term='JMock 2+'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Java 6+'/><title type='text'>Unit Tests (I): "mocking" la interacción</title><content type='html'>Indispensables. De entre todos los tipos de tests, son los más sencillos de escribir, aunque son los que permiten encontrar (a tiempo) la mayor cantidad de bugs.&lt;br /&gt;&lt;br /&gt;¿Qué es un Unit Test? Mejor contar qué hacen y no lo que son: &lt;b&gt;Un Unit Test debe probar un ÚNICO método de una ÚNICA clase&lt;/b&gt;. Simple. Sin excepciones &lt;a href="#note1"&gt;&lt;sup&gt;(*)&lt;/sup&gt;&lt;/a&gt;. El método a probar SIEMPRE debe tener un estado inicial conocido y un resultado esperado. ¡Siempre! Si cumple ambas premisas, el test podrá ser ejecutado de forma automática y sin depender de nadie &lt;a href="#note2"&gt;&lt;sup&gt;(**)&lt;/sup&gt;&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Considero que probar la interacción de una clase con el resto de componentes es uno de los sistemas que mejores resultados ofrece. Estas interacciones se "simulan" en el mismo test, utilizando mock-objects.&lt;br /&gt;&lt;br /&gt;Antes de empezar con algún ejemplo de Unit Test con Mock Objects, hay algunos consejos que nos pueden facilitar el trabajo:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Cada clase debe tener su Interfaz &lt;a href="#note3"&gt;&lt;sup&gt;(***)&lt;/sup&gt;&lt;/a&gt;, exceptuando las &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;java.lang.Exception&lt;/span&gt; y poco más.&lt;/li&gt;&lt;li&gt;Conocer el patrón de Inyección de Dependencias (&lt;a href="http://en.wikipedia.org/wiki/Dependency_injection"&gt;http://en.wikipedia.org/wiki/Dependency_injection&lt;/a&gt;).&amp;nbsp;&lt;/li&gt;&lt;li&gt;Acostumbrarse a las Expectations de los mock-objects: &lt;b&gt;cookbook de JMock&lt;/b&gt; (&lt;a href="http://www.jmock.org/cookbook.html"&gt;http://www.jmock.org/cookbook.html&lt;/a&gt;).&amp;nbsp;&lt;/li&gt;&lt;li&gt;El paquete de la clase "siendo probada" y el test deberían ser el mismo. Esto permitirá probar métodos &lt;b&gt;protected&lt;/b&gt; y &lt;b&gt;package&lt;/b&gt; sin demasiada &lt;i&gt;fontanería &lt;/i&gt;o mucho &lt;a href="http://en.wikipedia.org/wiki/Do_it_yourself"&gt;&lt;i&gt;apicoypalismo&lt;/i&gt;&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Para los siguientes ejemplos utilizaré &lt;b&gt;Java 6&lt;/b&gt;,&lt;b&gt; JUnit 4.7&lt;/b&gt; y &lt;b&gt;JMock 2.5.1&lt;/b&gt;, aunque debería funcionar para cualquier versión &lt;i&gt;Java 5+&lt;/i&gt;, &lt;i&gt;JUnit 4+&lt;/i&gt; y &lt;i&gt;JMock 2+&lt;/i&gt;.&lt;br /&gt;&lt;br /&gt;Utilizando un &lt;a href="http://users.last.fm/%7Emarc/doc/#sect2"&gt;ejemplo parecido&lt;/a&gt; a los de &lt;a href="http://distribuint.blogspot.com/"&gt;Marc&lt;/a&gt;, imaginemos que tenemos que implementar el siguiente método:&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;public Interface IBombService {&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Check if the country with countryName should be bombed.&lt;br /&gt;   *&lt;br /&gt;   * @return true if the country should be bombed, or false if not.&lt;br /&gt;   *&lt;br /&gt;   * @throws NoSuchElementException If no country with the given name exists.&lt;br /&gt;   */&lt;br /&gt;  public boolean shouldBeBombed(String countryName) throws NoSuchElementException;&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Sabemos que el método deberá consultar si el nombre del país es válido. Luego deberá mirar el último ataque en nuestra base de datos, por si es demasiado pronto para volver a ser bombardeado. Finalmente deberá consultar un servicio web externo para verificar si debe ser atacado o no. Nada de &lt;strike&gt;HelloWorld&lt;/strike&gt;... un caso más cercano a "nuestra" realidad.&lt;br /&gt;&lt;br /&gt;La estructura que recomiendo usar para cualquier test unitario con JMock es la siguiente:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;package cat.edra.impl;&lt;br /&gt;&lt;br /&gt;import org.jmock.*;&lt;br /&gt;import org.junit.*;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Unit test for YetAnotherService methods.&lt;br /&gt; */&lt;br /&gt;public class YetAnotherServiceTest {&lt;br /&gt;&lt;br /&gt;    private YetAnotherService yetAnotherService = null;&lt;br /&gt;&lt;br /&gt;    private Mockery mockery = new Mockery();&lt;br /&gt;&lt;br /&gt;    // Interfaces de las dependencias del servicio&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        yetAnotherService = new YetAnotherService();&lt;br /&gt;&lt;br /&gt;        // Inicialización de cada uno de los mocks de las dependencias&lt;br /&gt;        // Inyección de cada una de las dependencias "mocked".&lt;br /&gt;&lt;br /&gt;        // Mock conditions for every test...&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;            // Expectations que deben cumplir TODOS los tests (cache, recuperar un objeto, ...)&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @After&lt;br /&gt;    public void tearDown() {&lt;br /&gt;        mockery.assertIsSatisfied();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testYetAnotherServiceCorrectFlow() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                // Lista de Expectations que debe cumplir este test concreto.&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        yetAnotherService.methodToTest(...);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test(expected = TheExpectedThrownException.class)&lt;br /&gt;    public void testYetAnotherServiceIfAnExceptionIsRaised() throws Exception {&lt;br /&gt;    [...]&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;En este punto, y aplicando algo de TDD, sería bueno plantearse los casos de prueba. Podríamos empezar implementando algunos de los siguientes:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;public void testShouldBeBombedIfCountryNameNotExists()...&lt;br /&gt;public void testShouldBeBombedIfDbException()...&lt;br /&gt;public void testShouldBeBombedIfExternalServiceException()...&lt;br /&gt;public void testShouldBeBombedIfExternalResponseIsTrue()...&lt;br /&gt;public void testShouldBeBombedIfExternalResponseIsFalse()...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;El límite está en nuestra imaginación y lo completa que sea nuestra implementación. Utilizar TDD al 100% nos permite implementar SÓLO el código que haga funcionar estos tests. Ni una línea más. Utilizar TDD a medias, nos permitirá ir añadiendo, poco a poco, nuevos casos de tests, aún a riesgo de implementar más líneas de código de las realmente necesarias. Sea como sea, los tests unitarios nos ayudarán (y mucho) a mejorar la calidad de nuestro código.&lt;br /&gt;&lt;br /&gt;Como ejemplo, veamos la implementación con mock-objects de uno de los casos correctos y de un caso de error con la base de datos:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java" name="code"&gt;    [...]&lt;br /&gt;&lt;br /&gt;    private BombService bombService = null;&lt;br /&gt;&lt;br /&gt;    @Before&lt;br /&gt;    public void setUp() {&lt;br /&gt;        bombService = new BombService();&lt;br /&gt;&lt;br /&gt;        countryDaoMock = mockery.mock(ICountryDao.class);&lt;br /&gt;        bombDaoMock = mockery.mock(IBombDao.class);&lt;br /&gt;        bombSoapClientMock = mockery.mock(BombSoapClient.class);&lt;br /&gt;&lt;br /&gt;        bombService.setCountryDao(countryDaoMock);&lt;br /&gt;        bombService.setBombDao(bombDaoMock);&lt;br /&gt;        bombService.setBombSoapClient(bombSoapClientMock);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @After&lt;br /&gt;    public void tearDown() {&lt;br /&gt;        mockery.assertIsSatisfied();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testShouldBeBombed() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                exactly(1).of(countryDaoMock).findCountryByName(with("Avalon"));&lt;br /&gt;                will(returnValue(new Country("1", "Avalon", "AVA")));&lt;br /&gt;&lt;br /&gt;                Calendar cal = Calendar.getInstance();&lt;br /&gt;                cal.set(2008, 0, 1);&lt;br /&gt;                exactly(1).of(bombDaoMock).getLastBombed(&lt;br /&gt;                        with(any(Country.class)));&lt;br /&gt;                will(returnValue(cal.getTime()));&lt;br /&gt;&lt;br /&gt;                exactly(1).of(bombSoapClientMock).isPendingAttack(&lt;br /&gt;                        with("Avalon"));&lt;br /&gt;                will(returnValue(true));&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        boolean result = bombService.shouldBeBombed("Avalon");&lt;br /&gt;        assertTrue(result);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test(expected = NoSuchElementException.class)&lt;br /&gt;    public void testShouldBeBombedIfCountryNameNotExists() throws Exception {&lt;br /&gt;        mockery.checking(new Expectations() {&lt;br /&gt;            {&lt;br /&gt;                exactly(1).of(countryDaoMock).findCountryByName(with("Avalon"));&lt;br /&gt;                will(returnValue(null));&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        bombService.shouldBeBombed("Moon");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    [...]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Una vez uno se acostumbra a la sintaxis de JMock (o equivalentes), es muy sencillo realizar tests unitarios que validen el comportamiento de nuestro método ante cada excepción y respuesta posible de sus dependencias.&lt;br /&gt;&lt;br /&gt;La clase que estamos probando utiliza directamente su implementación &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;private BombService bombService = null;&lt;/span&gt;&lt;span style="font-family: inherit;"&gt; en lugar de su interfaz.&lt;/span&gt; Cada test se encarga de reiniciar el estado original del objeto en el método &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;setUp()&lt;/span&gt; y validar que todo finaliza correctamente en el &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;tearDown()&lt;/span&gt;&lt;span style="font-family: inherit;"&gt;. &lt;/span&gt;El resto de dependencias sólo utilizan su interfaz, que se convertirá en el contrato que deberán seguir las implementaciones para ser compatibles con nuestro código (tanto los casos correctos, como los errores).&lt;br /&gt;&lt;br /&gt;La única premisa para implementar estos tests es utilizar (o crear) las interfaces de cada una de las dependencias, con los métodos que pueden ser invocados desde el nuestro. Eso sí, sin implementación, ya que el comportamiento que esperamos de ellas se define en las &lt;b&gt;Expectations&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;Llegados a este punto, quedan muchos temas abiertos, que irán apareciendo en próximas entregas:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;¿Qué ocurre si alguna de mis dependencias no tiene interfaz?&lt;/li&gt;&lt;li&gt;¿Cómo se prueban capas de acceso a datos o servicios web, que no tienen un estado inicial conocido?&amp;nbsp;&lt;/li&gt;&lt;li&gt;¿Qué hacer si las Expectations de un test crecen desmesuradamente?&lt;/li&gt;&lt;li&gt;...&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Pero esto ya será carne de otros tutoriales... Mientras tanto, &lt;b&gt;let's test!&lt;/b&gt; :)&lt;br /&gt;&lt;br /&gt;&lt;a name="note1"&gt;&lt;sup&gt;(*)&lt;/sup&gt;&lt;/a&gt; Aunque puedan existir casos justificados de tests unitarios contra más de una clase o método, no es ni normal ni recomendable que abunden en nuestro código.&lt;br /&gt;&lt;a name="note2"&gt;&lt;sup&gt;(**)&lt;/sup&gt;&lt;/a&gt; En próximos posts contaré qué pasa con los tests que no son fácilmente automatizables (como los que acceden a base de datos o sistemas de colas).&lt;br /&gt;&lt;a name="note3"&gt;&lt;sup&gt;(***)&lt;/sup&gt;&lt;/a&gt; Sólo es aconsejable  dejar sin interfaz aquellas clases de poca entidad, como algún que otro Helper o similares.&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-6237364645929369645?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2009/10/unit-tests-i-mocking-la-interaccion.html#comment-form' title='2 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/6237364645929369645'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/6237364645929369645'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2009/10/unit-tests-i-mocking-la-interaccion.html' title='Unit Tests (I): &quot;mocking&quot; la interacción'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-809960945490047135.post-5214715695177614820</id><published>2009-09-15T00:00:00.045+02:00</published><updated>2009-09-15T20:51:23.339+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Testing'/><category scheme='http://www.blogger.com/atom/ns#' term='TDD'/><category scheme='http://www.blogger.com/atom/ns#' term='Intro'/><title type='text'>¿Introducción?</title><content type='html'>Hace tiempo que las siglas &lt;acronym title="Test Driven Development"&gt;TDD&lt;/acronym&gt; están de moda. &lt;b&gt;Test Driven Development&lt;/b&gt; por todos lados. Pero cuando uno se decide a ponerlo en práctica, lo único que encuentra por Mr. Google son ejemplos sencillos (HelloWorld, PetClinic, el Hospital y sus pacientes, o la última gran incorporación: Twitter en 15 minutos).&lt;br /&gt;&lt;br /&gt;Por desgracia, cuando un developer decide probar su código, nunca (enfatizo: &lt;b&gt;¡nunca!&lt;/b&gt;) sirven de nada estos casos tan sencillos. A la hora de la verdad, tenemos varias bases de datos, repositorios de contenidos, conexiones a servicios &lt;a href="http://en.wikipedia.org/wiki/SOAP"&gt;SOAP&lt;/a&gt; (escalofrío), a servicios &lt;a href="http://en.wikipedia.org/wiki/Representational_State_Transfer"&gt;REST&lt;/a&gt;... Casos difíciles de probar, que agradecen un conjunto de buenas prácticas o ejemplos parecidos.&lt;br /&gt;&lt;br /&gt;Es muy difícil encontrar información clara sobre por dónde empezar. Sobre dónde buscar. Sobre qué debería hacer un test (o lo que nunca debería hacer). Uno acaba teniendo la impresión que probar código de manera correcta y eficaz es un arte que se aprende con el tiempo, a base de pruebas y (muchos) errores.&lt;br /&gt;&lt;br /&gt;Cansado de esa sensación, y después de cometer errores y más errores, quería compartir en este blog algunos ejemplos que puedan ayudar al desarrollo orientado a pruebas. Desde &lt;a href="http://en.wikipedia.org/wiki/Unit_testing"&gt;tests unitarios&lt;/a&gt; (auténtico pilar del desarrollo) a &lt;a href="http://en.wikipedia.org/wiki/Destructive_testing#Destructive_software_testing"&gt;tests destructivos&lt;/a&gt;, intentando utilizar ejemplos reales.&lt;br /&gt;&lt;br /&gt;Let's test! :)&lt;div class="blogger-post-footer"&gt;... probando, probando ...&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/809960945490047135-5214715695177614820?l=testingalittle.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='text/html' href='http://testingalittle.blogspot.com/2009/09/introduccion.html#comment-form' title='3 comentarios'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/5214715695177614820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/809960945490047135/posts/default/5214715695177614820'/><link rel='alternate' type='text/html' href='http://testingalittle.blogspot.com/2009/09/introduccion.html' title='¿Introducción?'/><author><name>Edraí Brosa</name><uri>http://www.blogger.com/profile/09711909285847311615</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry></feed>
