Diferencia entre revisiones de «Captura De Parametros»

De Dos Ideas.
Saltar a: navegación, buscar
(Ver también)
 
(No se muestran 11 ediciones intermedias de 2 usuarios)
Línea 1: Línea 1:
Hay situaciones en las que el estamos realizando un testeo con un [[Mock Object]] y queremos capturar un parámetro que recibe el mismo.
+
Hay situaciones en las que el estamos realizando un testeo con un [[Mock Object]] y queremos capturar un parámetro que recibe el mismo.  
  
Esta situación suele producirse cuando hay un objeto que se crea dentro de los objetos que estamos probando y llega como parámetro a algún [[Mock Object]]. Hay unas cuantas condiciones que podemos pedir a EasyMock y Mockito que validen como parámetro esperado como por ejemplo igualdad, expresiones regulares, no nulo, nulo, algunas comprobaciones con Strings y primitivos, etc. Pero incluso a veces esto no alcanza. El objeto fue creado dentro de otro objeto, es decir que no podemos ni inyectarlo desde fuera ni conseguirlo por medios naturales y queremos inspeccionar sus atributos y realizar una serie de asserts. Es ahí cuando conseguir una referencia al mismo nos resulta indispensable.
+
Esta situación suele producirse cuando hay un objeto que se crea dentro de los objetos que estamos probando y llega como parámetro a algún [[Mock Object]]. Hay unas cuantas condiciones que podemos pedir a EasyMock y Mockito que validen como parámetro esperado como por ejemplo igualdad, expresiones regulares, no nulo, nulo, algunas comprobaciones con Strings y primitivos, etc. Pero incluso a veces esto no alcanza. El objeto fue creado dentro de otro objeto, es decir que no podemos ni inyectarlo desde fuera ni conseguirlo por medios naturales y queremos inspeccionar sus atributos y realizar una serie de asserts. Es ahí cuando conseguir una referencia al mismo nos resulta ütil.  
  
==Ejemplo==
+
== Ejemplo ==
===El escenario===
 
Supongamos la siguiente interfaz de negocio:
 
  
<code java>
+
=== El escenario  ===
 +
 
 +
Supongamos la siguiente interfaz de negocio:
 +
 
 +
<code java="java">
 
  public interface PlanInvasionBo {
 
  public interface PlanInvasionBo {
 
     public InvasorDto findInvasorMasPeligroso();
 
     public InvasorDto findInvasorMasPeligroso();
 
  }
 
  }
</code>
+
</code>  
  
... la siguiente interfaz de DAO
+
... la siguiente interfaz de DAO  
  
<code java>
+
<code java="java">
 
  public interface PlanInvasionDao {
 
  public interface PlanInvasionDao {
 
     public Invasor findInvasorPorFiltro(Filtro filtro);
 
     public Invasor findInvasorPorFiltro(Filtro filtro);
 
  }
 
  }
</code>
+
</code>  
  
... y el siguiente filtro
+
... y el siguiente filtro  
  
<code java>
+
<code java="java">
 
  public class Filtro {
 
  public class Filtro {
  
Línea 32: Línea 34:
 
     // sus metodos accesores
 
     // sus metodos accesores
 
  }
 
  }
</code>
+
</code>  
  
Dentro de la implementación de la clase PlanInvasionBo se crea una instancia de Filtro que se utiliza para comunicarse con el DAO pero no es externalizada en ningún momento (salvo como parámetro en la llamada al método del DAO) ni existe la posiblidad de inyectarla desde fuera.
+
Dentro de la implementación de la clase PlanInvasionBo se crea una instancia de Filtro que se utiliza para comunicarse con el DAO pero no es externalizada en ningún momento (salvo como parámetro en la llamada al método del DAO) ni existe la posiblidad de inyectarla desde fuera.  
  
<code java>
+
<code java="java">
 
  public class PlanInvasorBoImpl implements PlanInvasor {
 
  public class PlanInvasorBoImpl implements PlanInvasor {
  
Línea 59: Línea 61:
  
 
  }
 
  }
</code>
+
</code>  
 +
 
 +
=== El objetivo  ===
  
===El objetivo===
+
El objetivo que nos planteamos es poder inspeccionar en un test que los valores del objeto Velocidad estén dentro de ciertos límites.
  
El objetivo que nos planteamos es poder inspeccionar en un test que los valores del objeto Velocidad estén dentro de ciertos límites.
+
=== El problema  ===
  
===El problema===
+
El problema que tenemos es que el objeto Velocidad se crea a partir de unos métodos privados de PlanInvasionBo y está dentro de otro objeto que se crea también dentro de PlanInvasionBo al que tampoco tenemos acceso. La única comunicación de este objeto con el exterior es en forma de parámetro a través de la llamada al método del DAO.
  
El problema que tenemos es que el objeto Velocidad se crea a partir de unos métodos privados de PlanInvasionBo y está dentro de otro objeto que se crea también dentro de PlanInvasionBo al que tampoco tenemos acceso.
+
= Una posible solución  =
La única comunicación de este objeto con el exterior es en forma de parámetro a través de la llamada al método del DAO.
 
  
=Una posible solución=
+
Una posible solución a nuestro objetivo es capturar el parámetro Filtro que recibe el método del DAO que en el caso del test será un [[Mock Object]].
  
Una posible solución a nuestro objetivo es capturar el parámetro Filtro que recibe el método del DAO que en el caso del test será un [[Mock Object]].
+
== Captura de parámetro con EasyMock  ==
  
==Captura de parámetro con EasyMock==
+
Para capturar un parámetro con [[EasyMock]] tenemos que:
  
La forma de capturar un parámetro con [[EasyMock]] es creando un objeto Capture parametrizado con la clase que va a capturar.
+
*Crear un objeto Capture parametrizado con la clase que va a capturar.  
Luego, en la maquetación del método, se utiliza el método estático EasyMock.capture que recibe al capturador.
+
*En el expect del método que recibe el parámetro a capturar se utiliza el método estático EasyMock.capture que recibe al capturador.  
Luego de ejercitar el método podemos pedirle el valor capturado al capturador.
+
*Pedirle al capturador el valor capturado.
  
<code java>
+
<code java="java">
 
  package test.com.dosideas.mock.business.impl;
 
  package test.com.dosideas.mock.business.impl;
  
Línea 107: Línea 110:
 
         replay(mockDao);
 
         replay(mockDao);
  
         InvasorDto resultado = planInvasionBo.findInvasorById(id);
+
         Invasor resultado = planInvasionBo.findInvasorMasPeligroso();
  
 
         verify(mockDao);
 
         verify(mockDao);
Línea 122: Línea 125:
 
     }
 
     }
 
  }
 
  }
</code>
+
</code>  
 +
 
 +
== Captura de parámetro con Mockito  ==
 +
 
 +
{{aviso|texto=A partir de la version 1.8 se implementó una manera más simple de captura de parámetros.<br>
 +
Ver [http://mockito.googlecode.com/svn/branches/1.8.0/javadoc/org/mockito/Mockito.html#15 15. (**New**) Capturing arguments for further assertions]}}
 +
 
 +
Para realizar la captura de parámetros con Mockito podemos utilizar una funcionalidad que tiene que la denomina "Stubbing with callbacks". Lo que hace es darnos la posibilidad de pasarle un callback a la llamada de un método maquetado. En nuestro caso lo que haremos en ese callback es capturar el parámetro que recibe al ser llamado. Los pasos son los siguientes:
 +
 
 +
*Crear un atributo de la clase de Test que contendrá el parámetro capturado.
 +
*Crear un callback para el método a maquetar.
 +
*Capturar el parámetro en cuestión dentro del callback y asignarlo al atributo del Test.
 +
 
 +
<code java="java">
 +
package test.com.dosideas.mock.business.impl;
 +
 
 +
//imports varios...
 +
import static org.mockito.Mockito.*;
 +
 
 +
public class PlanInvasionBoImplTest extends TestCase {
 +
 
 +
    private PlanInvasionBoImpl planInvasionBo;
 +
    @Mock private PlanInvasionDao daoMock;
 +
    private Filtro filtroCapturado;
 +
 
 +
    protected void setUp() throws Exception {
 +
        MockitoAnnotations.initMocks(this);
 +
        planInvasionBo = new PlanInvasionBoImpl();
 +
        planInvasionBo.setPlanInvasionDao(daoMock);
 +
 
 +
 
 +
    }
 +
 
 +
    public void testFindInvasorMasPeligroso() {
 +
 
 +
        Invasor invasor = new Invasor(id, "Zim");
 +
 
 +
        //crear el callback para el metodo findInvasorPorFiltro()
 +
        doAnswer(new Answer<Object>() {
 +
          public Invasor answer(InvocationOnMock invocation) throws Throwable {
 +
            filtroCapturado = (Filtro)invocation.getArguments()[0];
 +
            return invasor;         
 +
          }
 +
        }).when(daoMock).findInvasorPorFilro(any(Filtro.class));
 +
 
 +
        InvasorDto resultado = planInvasionBo.findInvasorById(id);
 +
 
 +
        // los assert tipicos
  
==Captura de parámetro con Mockito==
+
        // los assert sobre el Filtro capturado
 +
        assertNotNull(filtroCapturado);
 +
        assertTrue(filtroCapturado.getVelocidad() < VELOCIDAD_MAXIMA);
 +
        // El resto de los asserts necesarios sobre el filtro
  
(Agregar descripcion y codigo de Mockito)
+
    }
 +
}
 +
</code>
  
===Extensión del Matcher de Mockito===
+
= Ver también  =
  
(Agregar descripcion y codigo del Matcher capturador)
+
*[[EasyMock]]
 +
*[[Mockito]]
 +
*[[MockEjb]]
 +
*[[Prueba Unitaria]]
 +
*[http://www.easymock.org/ Web oficial de EasyMock]
 +
*[http://today.java.net/pub/a/today/2006/06/20/getting-started-with-easymock-2.html Getting started with EasyMock2]
  
=Ver también=
+
[[Category:TDD]]
* [[EasyMock]]
 
* [[Mockito]]
 
* [[MockEjb]]
 
* [[Prueba Unitaria]]
 
* [http://www.easymock.org/ Web oficial de EasyMock]
 
* [http://today.java.net/pub/a/today/2006/06/20/getting-started-with-easymock-2.html Getting started with EasyMock2]
 

Revisión actual del 14:30 23 oct 2009

Hay situaciones en las que el estamos realizando un testeo con un Mock Object y queremos capturar un parámetro que recibe el mismo.

Esta situación suele producirse cuando hay un objeto que se crea dentro de los objetos que estamos probando y llega como parámetro a algún Mock Object. Hay unas cuantas condiciones que podemos pedir a EasyMock y Mockito que validen como parámetro esperado como por ejemplo igualdad, expresiones regulares, no nulo, nulo, algunas comprobaciones con Strings y primitivos, etc. Pero incluso a veces esto no alcanza. El objeto fue creado dentro de otro objeto, es decir que no podemos ni inyectarlo desde fuera ni conseguirlo por medios naturales y queremos inspeccionar sus atributos y realizar una serie de asserts. Es ahí cuando conseguir una referencia al mismo nos resulta ütil.

Ejemplo

El escenario

Supongamos la siguiente interfaz de negocio:

public interface PlanInvasionBo {
    public InvasorDto findInvasorMasPeligroso();
}

... la siguiente interfaz de DAO

public interface PlanInvasionDao {
    public Invasor findInvasorPorFiltro(Filtro filtro);
}

... y el siguiente filtro

public class Filtro {
    private Fuerza fuerza;
    private Velocidad velocidad;
    private Destreza destreza;
    // sus metodos accesores
}

Dentro de la implementación de la clase PlanInvasionBo se crea una instancia de Filtro que se utiliza para comunicarse con el DAO pero no es externalizada en ningún momento (salvo como parámetro en la llamada al método del DAO) ni existe la posiblidad de inyectarla desde fuera.

public class PlanInvasorBoImpl implements PlanInvasor {
    private PlanInvasionDao planInvasionDao;
    // otros atributos
    
    public Invasor findInvasorMasPeligroso() {
        // Realiza ciertos guarismos internos 
        // para determinar la Fuerza, Velocidad y Destreza
        Fuerza fuerza = calcularFuerza();
        Velocidad velocidad = calcularVelocidad();
        Destreza destreza = calcularDestreza();
        // Utiliza los valores determinados para construir un Filtro
        Filtro filtro = new Filtro(fuerza, velocidad, destreza);
        // Llama al DAO con el filtro construido internamente
        return planInvasionDao.findInvasorPorFiltro(filtro);
    }
    // otros metodos
}

El objetivo

El objetivo que nos planteamos es poder inspeccionar en un test que los valores del objeto Velocidad estén dentro de ciertos límites.

El problema

El problema que tenemos es que el objeto Velocidad se crea a partir de unos métodos privados de PlanInvasionBo y está dentro de otro objeto que se crea también dentro de PlanInvasionBo al que tampoco tenemos acceso. La única comunicación de este objeto con el exterior es en forma de parámetro a través de la llamada al método del DAO.

Una posible solución

Una posible solución a nuestro objetivo es capturar el parámetro Filtro que recibe el método del DAO que en el caso del test será un Mock Object.

Captura de parámetro con EasyMock

Para capturar un parámetro con EasyMock tenemos que:

  • Crear un objeto Capture parametrizado con la clase que va a capturar.
  • En el expect del método que recibe el parámetro a capturar se utiliza el método estático EasyMock.capture que recibe al capturador.
  • Pedirle al capturador el valor capturado.

package test.com.dosideas.mock.business.impl;
//imports varios...
import static org.easymock.EasyMock.*;
public class PlanInvasionBoImplTest extends TestCase {
    private PlanInvasionBoImpl planInvasionBo;
    private PlanInvasionDao daoMock;
    protected void setUp() throws Exception {
        daoMock = createMock(PlanInvasionDao.class);
        planInvasionBo = new PlanInvasionBoImpl();
        planInvasionBo.setPlanInvasionDao(daoMock);
    }
    public void testFindInvasorMasPeligroso() {
        Invasor invasor = new Invasor(id, "Zim");
        // Crear un Capture para utilizar en la maquetacion del metodo
        Capture<Filtro> caturaFiltro = new Capture<Filtro>();
        expect(mockDao.findInvasorPorFilro(capture(capturaFiltro)).andReturn(invasor);
        replay(mockDao);
        Invasor resultado = planInvasionBo.findInvasorMasPeligroso();
        verify(mockDao);
        // los assert tipicos
        
        // Conseguir la referencia al filtro que llego como parametro
        // al metodo del daoMock
        Filtro filtro = capturaFiltro.getValue();
        assertNotNull(filtro);
        assertTrue(filtro.getVelocidad() < VELOCIDAD_MAXIMA);
        // El resto de los asserts necesarios sobre el filtro
    }
}

Captura de parámetro con Mockito

A partir de la version 1.8 se implementó una manera más simple de captura de parámetros.
Ver 15. (**New**) Capturing arguments for further assertions

Para realizar la captura de parámetros con Mockito podemos utilizar una funcionalidad que tiene que la denomina "Stubbing with callbacks". Lo que hace es darnos la posibilidad de pasarle un callback a la llamada de un método maquetado. En nuestro caso lo que haremos en ese callback es capturar el parámetro que recibe al ser llamado. Los pasos son los siguientes:

  • Crear un atributo de la clase de Test que contendrá el parámetro capturado.
  • Crear un callback para el método a maquetar.
  • Capturar el parámetro en cuestión dentro del callback y asignarlo al atributo del Test.

package test.com.dosideas.mock.business.impl;
//imports varios...
import static org.mockito.Mockito.*;
public class PlanInvasionBoImplTest extends TestCase {
    private PlanInvasionBoImpl planInvasionBo;
    @Mock private PlanInvasionDao daoMock;
    private Filtro filtroCapturado;
    protected void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        planInvasionBo = new PlanInvasionBoImpl();
        planInvasionBo.setPlanInvasionDao(daoMock);


    }
    public void testFindInvasorMasPeligroso() {
        Invasor invasor = new Invasor(id, "Zim");
        //crear el callback para el metodo findInvasorPorFiltro()
        doAnswer(new Answer<Object>() {
          public Invasor answer(InvocationOnMock invocation) throws Throwable {
            filtroCapturado = (Filtro)invocation.getArguments()[0];
            return invasor;           
          }
        }).when(daoMock).findInvasorPorFilro(any(Filtro.class));
        InvasorDto resultado = planInvasionBo.findInvasorById(id);
        // los assert tipicos
        // los assert sobre el Filtro capturado
        assertNotNull(filtroCapturado);
        assertTrue(filtroCapturado.getVelocidad() < VELOCIDAD_MAXIMA);
        // El resto de los asserts necesarios sobre el filtro
    }
}

Ver también