Diferencia entre revisiones de «Captura De Parametros»
(→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 | + | 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 == |
− | |||
− | |||
− | <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 que nos planteamos es poder inspeccionar en un test que los valores del objeto Velocidad estén dentro de ciertos límites. | |
− | El | + | === 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. | |
− | <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); | ||
− | + | 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 | ||
− | + | // los assert sobre el Filtro capturado | |
+ | assertNotNull(filtroCapturado); | ||
+ | assertTrue(filtroCapturado.getVelocidad() < VELOCIDAD_MAXIMA); | ||
+ | // El resto de los asserts necesarios sobre el filtro | ||
− | + | } | |
+ | } | ||
+ | </code> | ||
− | = | + | = Ver también = |
− | + | *[[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] | ||
− | + | [[Category:TDD]] | |
− | |||
− | |||
− | |||
− | |||
− | |||
− |
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.
Contenido
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
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
} }