Diferencia entre revisiones de «Mockito»
(Página nueva: Mockito es una librería Java para la creación de Mock Objects muy usados para el testeo unitario en TDD, basado en EasyMock. Mockito fue creado con el objetivo de ...) |
(→Ejemplo de Mockito para mockear appender de log4j) |
||
(No se muestran 53 ediciones intermedias de 16 usuarios) | |||
Línea 1: | Línea 1: | ||
− | Mockito es una librería [[Java]] para la creación de [[Mock | + | Mockito es una librería [[Java]] para la creación de [[Mock Object]] muy usados para el testeo unitario en [[Test Driven Development]], basado en [[EasyMock]]. |
Mockito fue creado con el objetivo de simplificar y solucionar algunos de los temas antes mencionados. EasyMock y Mockito puede hacer exactamente las mismas cosas, pero Mockito tiene un API más natural y práctico de usar. | Mockito fue creado con el objetivo de simplificar y solucionar algunos de los temas antes mencionados. EasyMock y Mockito puede hacer exactamente las mismas cosas, pero Mockito tiene un API más natural y práctico de usar. | ||
Línea 22: | Línea 22: | ||
//verificamos que se hayan invocado los métodos | //verificamos que se hayan invocado los métodos | ||
verify(instance).get(0); | verify(instance).get(0); | ||
+ | </code> | ||
+ | |||
+ | ==Más de una invocación a un mismo método== | ||
+ | |||
+ | Algunas veces necesitamos que un método retorne un valor y que en la segunda invocación retorne nulo o una excepción. Para eso, anidamos los retornos: | ||
+ | |||
+ | <code html4strict> | ||
+ | |||
+ | //Retornos para las 2 invocaciones | ||
+ | doReturn("algo").doReturn(null).when(instance).scrollNext(); | ||
+ | |||
+ | //ejecutamos la lógica a probar | ||
+ | instance.unMetodoQueUsaScroll(); | ||
+ | |||
+ | //verificamos que se haya invocado el mismo método dos veces | ||
+ | verify(instance, times(2)).scrollNext(); | ||
+ | |||
+ | </code> | ||
+ | |||
+ | * [http://mockito.googlecode.com/svn/branches/1.5/javadoc/org/mockito/Mockito.html Leer más en Mockito API] | ||
+ | |||
+ | ==Ejemplos de Mockito junto a PowerMock== | ||
+ | |||
+ | ===Invocación a un método estático=== | ||
+ | |||
+ | Para mockear métodos estáticos, según el [http://code.google.com/p/mockito/wiki/FAQ FAQ] de mockito, esto no es posible. Pero nombra 2 framewors que pueden resultarnos útil [http://jmockit.dev.java.net/ JMockit] y [http://code.google.com/p/powermock PowerMock] | ||
+ | |||
+ | <code java> | ||
+ | @RunWith(PowerMockRunner.class) | ||
+ | @PrepareForTest(ClaseConMetodosEstaticos.class) | ||
+ | public class DosIdeasTestCase { | ||
+ | @Test | ||
+ | public void testMetodo_conInvocacionAMetodosEstaticos() { | ||
+ | |||
+ | //PowerMockito mockea los metodos estaticos que se encuentren en la | ||
+ | //clase ClaseConMetodosEstaticos | ||
+ | PowerMockito.mockStatic(ClaseConMetodosEstaticos.class); | ||
+ | |||
+ | //Con mockito determinamos el retorno del metodo estatico | ||
+ | when(ClaseConMetodosEstaticos.firstStaticMethod(param)).thenReturn(value); | ||
+ | |||
+ | // ejecucion | ||
+ | instance.invocarMetodoEstatico(); | ||
+ | |||
+ | //Por cada metodo estatico que se quiere verificar se debe invocar | ||
+ | //a PowerMockito.verifyStatic(). | ||
+ | PowerMockito.verifyStatic(); | ||
+ | ClaseConMetodosEstaticos.secondStaticMethod(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===Invocación a un método privado=== | ||
+ | |||
+ | <code java> | ||
+ | public class PruebaMetodoPrivado { | ||
+ | |||
+ | public String decirHola(String nombre) { | ||
+ | return decirHolaNombre(nombre); | ||
+ | } | ||
+ | |||
+ | private String decirHolaNombre(String nombre) { | ||
+ | return "Hola "+nombre; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code java> | ||
+ | @RunWith(PowerMockRunner.class) | ||
+ | @PrepareForTest(PruebaMetodoPrivado.class) | ||
+ | public class PruebaMetodoPrivadoTest { | ||
+ | |||
+ | @Spy | ||
+ | PruebaMetodoPrivado metodoPrivado = new PruebaMetodoPrivado(); | ||
+ | |||
+ | @Test | ||
+ | public void mockMetodoPrivado() throws Exception { | ||
+ | |||
+ | Assert.assertEquals("Hola Juan", metodoPrivado.decirHola("Juan")); | ||
+ | doReturn("Chau Juan").when(metodoPrivado,"decirHolaNombre","Juan"); | ||
+ | Assert.assertEquals("Chau Juan",metodoPrivado.decirHola("Juan")); | ||
+ | verifyPrivate(metodoPrivado, | ||
+ | Mockito.times(2)).invoke("decirHolaNombre","Juan"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ===Otra manera de invocar a un metodo privado (Bypass encapsulation) === | ||
+ | |||
+ | Para invocar a un metodo privado se puede usar el metodo de PowerMock '''Whitebox.invokeMethod(..)'''. | ||
+ | |||
+ | <code java> | ||
+ | public class PruebaMetodoPrivado2 { | ||
+ | |||
+ | .... | ||
+ | |||
+ | private int sumar(int a, int b) { | ||
+ | return a+b; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code java> | ||
+ | import org.powermock.reflect.Whitebox; | ||
+ | |||
+ | public class PruebaMetodoPrivado2Test { | ||
+ | |||
+ | private PruebaMetodoPrivado2 instance; | ||
+ | |||
+ | @Before | ||
+ | public void setup() throws Exception { | ||
+ | instance = new PruebaMetodoPrivado2(); | ||
+ | } | ||
+ | |||
+ | @Test | ||
+ | public void sumar_conParametrosEnteros_retornaSuma() throws Exception { | ||
+ | |||
+ | int respuestaEsperada = 4; | ||
+ | |||
+ | int respuestaObtenida = Whitebox.<Integer> invokeMethod(instance, "sumar", 1, 3); | ||
+ | |||
+ | Assert.assertEquals(respuestaEsperada, respuestaObtenida); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ===Inicialización de atributos estaticos=== | ||
+ | |||
+ | <code java> | ||
+ | public class PruebaInicializacionEstaticos { | ||
+ | |||
+ | private static final String atributo = new String("CHAU"); | ||
+ | |||
+ | public String crearObjetoLista() { | ||
+ | return atributo; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code java> | ||
+ | @RunWith(PowerMockRunner.class) | ||
+ | @SuppressStaticInitializationFor("PruebaInicializacionEstaticos") | ||
+ | @PrepareForTest(PruebaInicializacionEstaticos.class) | ||
+ | public class PruebaMetodoPrivadoTest { | ||
+ | |||
+ | |||
+ | PruebaInicializacionEstaticos pruebaInicializacionEstaticos = | ||
+ | new PruebaInicializacionEstaticos(); | ||
+ | |||
+ | @Test | ||
+ | public void controlCreacionLista() throws Exception { | ||
+ | Assert.assertNull(pruebaInicializacionEstaticos.retornarAtrinbuto()); | ||
+ | String s = "Hola"; | ||
+ | Whitebox.setInternalState(pruebaInicializacionEstaticos.class,"atributo", s); | ||
+ | Assert.assertEquals(s, pruebaInicializacionEstaticos.retornarAtrinbuto()); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ===Captura creación de objetos=== | ||
+ | |||
+ | <code java> | ||
+ | public class PruebaCreacionObjeto { | ||
+ | |||
+ | public List crearObjetoLista() { | ||
+ | return new ArrayList(); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <code java> | ||
+ | @RunWith(PowerMockRunner.class) | ||
+ | @PrepareForTest(PruebaCreacionObjeto.class) | ||
+ | public class PruebaMetodoPrivadoTest { | ||
+ | |||
+ | @Spy | ||
+ | PruebaCreacionObjeto pruebaCreacionObjeto = new PruebaCreacionObjeto(); | ||
+ | |||
+ | @Test | ||
+ | public void controlCreacionLista() throws Exception { | ||
+ | List list = new ArrayList(); | ||
+ | list.add("Hola"); | ||
+ | whenNew(ArrayList.class).withNoArguments().thenReturn((ArrayList) list); | ||
+ | List resultado = pruebaCreacionObjeto.crearObjetoLista(); | ||
+ | Assert.assertEquals(list, resultado); | ||
+ | verifyNew(ArrayList.class); | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | Para mas ejemplos de Mockito junto a PowerMock ver los [http://code.google.com/p/powermock/wiki/MockitoUsage13 ejemplos] provisto por la documentación de PowerMock. | ||
+ | |||
+ | [http://www.dosideas.com/descargas/category/3-testing.html?download=40 Descargar proyecto NetBeans demo de los ejemplos] | ||
+ | |||
+ | ===PowerMockito & Cobertura=== | ||
+ | Existe un problema al utilizar PowerMockito con Cobertura (versión 1.8.4), este último no registra la corrida de los test con anotación @PrepareForTest, dando como resultados falta de cobertura. | ||
+ | Para solucionar este inconveniente solo hay que actualizar la versión de Cobertura. | ||
+ | |||
+ | ==Ejemplo de Mockito para mockear appender de log4j== | ||
+ | |||
+ | <code java> | ||
+ | //Inicializamos mock. | ||
+ | Appender mockAppender = mock(AppenderSkeleton.class); | ||
+ | //Inicializamos logger de la clase bajo test | ||
+ | Logger log = Logger.getLogger(ClaseATestear.class); | ||
+ | //Agregamos el mock del appender al logger. | ||
+ | log.addAppender(mockAppender); | ||
+ | //Seteamos el nivel de log que nos interesa recuperar. | ||
+ | log.setLevel(Level.DEBUG); | ||
+ | </code> | ||
+ | |||
+ | Para poder obtener los log que se produjeron utilizamos la clase ArgumentCaptor de mockito que nos permite, entre otras cosas, capturar los eventos de log. | ||
+ | |||
+ | <code java> | ||
+ | ArgumentCaptor loggingEventCaptor = ArgumentCaptor.forClass(LoggingEvent.class); | ||
+ | verify(mockAppender, times(1)).doAppend((LoggingEvent) loggingEventCaptor.capture()); | ||
+ | |||
+ | loggingEventCaptor.capture()); | ||
+ | </code> | ||
+ | |||
+ | Una posible forma de obtener un mensaje particular es iterando la lista de mensajes que nos da la clase capturada. | ||
+ | |||
+ | <code java> | ||
+ | List<LoggingEvent> loggingEvent = (List<LoggingEvent>) loggingEventCaptor.getAllValues(); | ||
+ | </code> | ||
+ | |||
+ | == BDDMockito == | ||
+ | BDDMokcito es una clase alternativa que ofrece [[Mockito]] para crear pruebas al estilo [[Behavior Driven Development]] (BDD) (es decir, con bloques dado / cuando / entonces). | ||
+ | |||
+ | El problema es que el API de Mockito usa la palabra "when" y no se integra bien con los comentarios //given //when //then (según BDD, el stubbing forma parte del área //given, y no del //when). | ||
+ | |||
+ | BDDMockito agrega una clase que tiene alias para los métodos de stub, usando given(Object). | ||
+ | |||
+ | <code java5> | ||
+ | import static org.mockito.BDDMockito.*; | ||
+ | |||
+ | Vendedor vendedor = mock(Vendedor.class); | ||
+ | Negocio negocio = new Negocio(vendedor); | ||
+ | |||
+ | @Test | ||
+ | public void comprarPan() throws Exception { | ||
+ | //given | ||
+ | given(vendedor.buscarPan()).willReturn(new Pan()); | ||
+ | |||
+ | //when | ||
+ | Compra compra = negocio.comprarPan(); | ||
+ | |||
+ | //then | ||
+ | assertThat(compra, contienePan()); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Stubbing con void y excepciones: | ||
+ | <code java5> | ||
+ | //given | ||
+ | willThrow(new RuntimeException("boo")).given(mock).foo(); | ||
+ | |||
+ | //when | ||
+ | Result result = sistemaBajoPrueba.ejecutar(); | ||
+ | |||
+ | //then | ||
+ | assertEquals(failure, result); | ||
</code> | </code> | ||
==Ver también== | ==Ver también== | ||
* [[EasyMock]] | * [[EasyMock]] | ||
− | * [http://www.dosideas.com/descargas/ | + | * [[Captura De Parametros]] |
+ | * [[Inyeccion De Mocks De Mockito Con Spring]] | ||
+ | * [[Test de componente para un cliente TCP]] | ||
+ | * [http://www.dosideas.com/descargas/category/3-testing.html?download=11%3Ademo-de-mockito Proyecto de ejemplos con Mockito] | ||
* [http://www.dosideas.com/java/242-mockito-o-basta-de-easymock.html Introducción a Mockito] | * [http://www.dosideas.com/java/242-mockito-o-basta-de-easymock.html Introducción a Mockito] | ||
+ | * [http://mockito.googlecode.com/svn/branches/1.8.0/javadoc/org/mockito/BDDMockito.html Javadoc de BDDMockito] | ||
+ | |||
+ | [[Category:TDD]] | ||
+ | [[Category:BDD]] | ||
+ | [[Category:JUnit]] |
Revisión actual del 13:09 11 nov 2011
Mockito es una librería Java para la creación de Mock Object muy usados para el testeo unitario en Test Driven Development, basado en EasyMock.
Mockito fue creado con el objetivo de simplificar y solucionar algunos de los temas antes mencionados. EasyMock y Mockito puede hacer exactamente las mismas cosas, pero Mockito tiene un API más natural y práctico de usar.
Contenido
Características principales
- Se pueden crear mocks de interfaces y clases concretas.
- Verificación de invocaciones (cantidad exacta, al menos una vez, órden de invocación, etc.)
- El stack trace se mantiene limpio, ya que los errores ocurren en los assert que se hagan (y no dentro del método bajo prueba, como en EasyMock).
- Un API más clara para crear stubs y verificaciones
Ejemplo de uso
import static org.mockito.Mockito.*;
.....
//creamos el mock y el stub
ArrayList instance = mock(ArrayList.class);
doReturn("hola mundo").when(instance).get(0);
//ejecutamos la lógica a probar instance.get(0);
//verificamos que se hayan invocado los métodos verify(instance).get(0);
Más de una invocación a un mismo método
Algunas veces necesitamos que un método retorne un valor y que en la segunda invocación retorne nulo o una excepción. Para eso, anidamos los retornos:
//Retornos para las 2 invocaciones doReturn("algo").doReturn(null).when(instance).scrollNext();
//ejecutamos la lógica a probar instance.unMetodoQueUsaScroll();
//verificamos que se haya invocado el mismo método dos veces verify(instance, times(2)).scrollNext();
Ejemplos de Mockito junto a PowerMock
Invocación a un método estático
Para mockear métodos estáticos, según el FAQ de mockito, esto no es posible. Pero nombra 2 framewors que pueden resultarnos útil JMockit y PowerMock
@RunWith(PowerMockRunner.class)
@PrepareForTest(ClaseConMetodosEstaticos.class)
public class DosIdeasTestCase {
@Test public void testMetodo_conInvocacionAMetodosEstaticos() {
//PowerMockito mockea los metodos estaticos que se encuentren en la //clase ClaseConMetodosEstaticos PowerMockito.mockStatic(ClaseConMetodosEstaticos.class);
//Con mockito determinamos el retorno del metodo estatico when(ClaseConMetodosEstaticos.firstStaticMethod(param)).thenReturn(value);
// ejecucion instance.invocarMetodoEstatico();
//Por cada metodo estatico que se quiere verificar se debe invocar //a PowerMockito.verifyStatic(). PowerMockito.verifyStatic(); ClaseConMetodosEstaticos.secondStaticMethod(); }
}
Invocación a un método privado
public class PruebaMetodoPrivado {
public String decirHola(String nombre) { return decirHolaNombre(nombre); }
private String decirHolaNombre(String nombre) { return "Hola "+nombre; }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(PruebaMetodoPrivado.class)
public class PruebaMetodoPrivadoTest {
@Spy PruebaMetodoPrivado metodoPrivado = new PruebaMetodoPrivado(); @Test public void mockMetodoPrivado() throws Exception {
Assert.assertEquals("Hola Juan", metodoPrivado.decirHola("Juan"));
doReturn("Chau Juan").when(metodoPrivado,"decirHolaNombre","Juan"); Assert.assertEquals("Chau Juan",metodoPrivado.decirHola("Juan")); verifyPrivate(metodoPrivado, Mockito.times(2)).invoke("decirHolaNombre","Juan"); }
}
Otra manera de invocar a un metodo privado (Bypass encapsulation)
Para invocar a un metodo privado se puede usar el metodo de PowerMock Whitebox.invokeMethod(..).
public class PruebaMetodoPrivado2 {
....
private int sumar(int a, int b) { return a+b; }
}
import org.powermock.reflect.Whitebox;
public class PruebaMetodoPrivado2Test {
private PruebaMetodoPrivado2 instance;
@Before public void setup() throws Exception { instance = new PruebaMetodoPrivado2(); } @Test public void sumar_conParametrosEnteros_retornaSuma() throws Exception {
int respuestaEsperada = 4;
int respuestaObtenida = Whitebox.<Integer> invokeMethod(instance, "sumar", 1, 3);
Assert.assertEquals(respuestaEsperada, respuestaObtenida);
}
}
Inicialización de atributos estaticos
public class PruebaInicializacionEstaticos {
private static final String atributo = new String("CHAU"); public String crearObjetoLista() { return atributo; }
}
@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("PruebaInicializacionEstaticos")
@PrepareForTest(PruebaInicializacionEstaticos.class)
public class PruebaMetodoPrivadoTest {
PruebaInicializacionEstaticos pruebaInicializacionEstaticos = new PruebaInicializacionEstaticos(); @Test public void controlCreacionLista() throws Exception {
Assert.assertNull(pruebaInicializacionEstaticos.retornarAtrinbuto());
String s = "Hola"; Whitebox.setInternalState(pruebaInicializacionEstaticos.class,"atributo", s); Assert.assertEquals(s, pruebaInicializacionEstaticos.retornarAtrinbuto()); }
}
Captura creación de objetos
public class PruebaCreacionObjeto {
public List crearObjetoLista() { return new ArrayList(); }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(PruebaCreacionObjeto.class)
public class PruebaMetodoPrivadoTest {
@Spy PruebaCreacionObjeto pruebaCreacionObjeto = new PruebaCreacionObjeto(); @Test public void controlCreacionLista() throws Exception { List list = new ArrayList(); list.add("Hola"); whenNew(ArrayList.class).withNoArguments().thenReturn((ArrayList) list); List resultado = pruebaCreacionObjeto.crearObjetoLista(); Assert.assertEquals(list, resultado); verifyNew(ArrayList.class); }
}
Para mas ejemplos de Mockito junto a PowerMock ver los ejemplos provisto por la documentación de PowerMock.
Descargar proyecto NetBeans demo de los ejemplos
PowerMockito & Cobertura
Existe un problema al utilizar PowerMockito con Cobertura (versión 1.8.4), este último no registra la corrida de los test con anotación @PrepareForTest, dando como resultados falta de cobertura. Para solucionar este inconveniente solo hay que actualizar la versión de Cobertura.
Ejemplo de Mockito para mockear appender de log4j
//Inicializamos mock.
Appender mockAppender = mock(AppenderSkeleton.class);
//Inicializamos logger de la clase bajo test
Logger log = Logger.getLogger(ClaseATestear.class);
//Agregamos el mock del appender al logger.
log.addAppender(mockAppender);
//Seteamos el nivel de log que nos interesa recuperar.
log.setLevel(Level.DEBUG);
Para poder obtener los log que se produjeron utilizamos la clase ArgumentCaptor de mockito que nos permite, entre otras cosas, capturar los eventos de log.
ArgumentCaptor loggingEventCaptor = ArgumentCaptor.forClass(LoggingEvent.class);
verify(mockAppender, times(1)).doAppend((LoggingEvent) loggingEventCaptor.capture());
loggingEventCaptor.capture());
Una posible forma de obtener un mensaje particular es iterando la lista de mensajes que nos da la clase capturada.
List<LoggingEvent> loggingEvent = (List<LoggingEvent>) loggingEventCaptor.getAllValues();
BDDMockito
BDDMokcito es una clase alternativa que ofrece Mockito para crear pruebas al estilo Behavior Driven Development (BDD) (es decir, con bloques dado / cuando / entonces).
El problema es que el API de Mockito usa la palabra "when" y no se integra bien con los comentarios //given //when //then (según BDD, el stubbing forma parte del área //given, y no del //when).
BDDMockito agrega una clase que tiene alias para los métodos de stub, usando given(Object).
import static org.mockito.BDDMockito.*; Vendedor vendedor = mock(Vendedor.class); Negocio negocio = new Negocio(vendedor); @Test public void comprarPan() throws Exception { //given given(vendedor.buscarPan()).willReturn(new Pan()); //when Compra compra = negocio.comprarPan(); //then assertThat(compra, contienePan()); }
Stubbing con void y excepciones:
//given willThrow(new RuntimeException("boo")).given(mock).foo(); //when Result result = sistemaBajoPrueba.ejecutar(); //then assertEquals(failure, result);