mockito Tutorial
Empezando con mockito
Buscar..
Observaciones
Mockito es un marco de simulación de Java que tiene como objetivo proporcionar la capacidad de escribir con claridad una prueba de unidad legible utilizando su API simple. Se diferencia de otros marcos de simulacros al dejar el patrón de esperar-ejecutar-verificar que la mayoría de los otros marcos utilizan.
En su lugar, solo conoce una forma de simular las clases e interfaces (no final) y permite verificar y apilar basándose en comparadores de argumentos flexibles.
La versión actual 1.10.19 se obtiene mejor utilizando maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
</dependency>
o gradle
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
La versión 2 todavía está en beta.
Versiones
Versión | Maven Central | Notas de lanzamiento | Fecha de lanzamiento |
---|---|---|---|
2.1.0 | mockito-core | cambios | 2016-10-04 |
1.10.19 | mockito-core | cambios | 2014-12-31 |
Prueba unitaria simple utilizando Mockito.
La clase que vamos a probar es:
public class Service {
private Collaborator collaborator;
public Service(Collaborator collaborator) {
this.collaborator = collaborator;
}
public String performService(String input) {
return collaborator.transformString(input);
}
}
Su colaborador es:
public class Collaborator {
public String transformString(String input) {
return doStuff();
}
private String doStuff() {
// This method may be full of bugs
. . .
return someString;
}
}
En nuestra prueba, queremos romper la dependencia de Collaborator
y sus errores, por lo que vamos a burlarnos de Collaborator
:
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
public class ServiceTest {
@Test
public void testPerformService() throws Exception {
// Configure mock
Collaborator collaboratorMock = mock(Collaborator.class);
doReturn("output").when(collaboratorMock).transformString("input");
// Perform the test
Service service = new Service(collaboratorMock);
String actual = service.performService("input");
// Junit asserts
String expected = "output";
assertEquals(expected, actual);
}
}
Usando las anotaciones de Mockito
La clase que vamos a probar es:
public class Service{
private Collaborator collaborator;
public Service(Collaborator collaborator){
this.collaborator = collaborator;
}
public String performService(String input){
return collaborator.transformString(input);
}
}
Su colaborador es:
public class Collaborator {
public String transformString(String input){
return doStuff();
}
private String doStuff()
{
// This method may be full of bugs
. . .
return someString;
}
}
En nuestra prueba, queremos romper la dependencia de Collaborator
y sus errores, por lo que vamos a burlarnos de Collaborator
. Usar la anotación de @Mock
es una manera conveniente de crear diferentes instancias de simulacros para cada prueba:
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
@Mock
private Collaborator collaboratorMock;
@InjectMocks
private Service service;
@Test
public void testPerformService() throws Exception {
// Configure mock
doReturn("output").when(collaboratorMock).transformString("input");
// Perform the test
String actual = service.performService("input");
// Junit asserts
String expected = "output";
assertEquals(expected, actual);
}
@Test(expected=Exception.class)
public void testPerformServiceShouldFail() throws Exception {
// Configure mock
doThrow(new Exception()).when(collaboratorMock).transformString("input");
// Perform the test
service.performService("input");
}
}
Mockito intentará resolver la inyección de dependencia en el siguiente orden:
- Inyección basada en el constructor: los simulacros se inyectan en el constructor con la mayoría de los argumentos (si no se pueden encontrar algunos argumentos, se pasan nulos). Si un objeto fue creado exitosamente a través del constructor, entonces no se aplicarán otras estrategias.
- Inyección a base de Setter - los mock son inyectados por tipo. Si hay varias propiedades del mismo tipo, los nombres de las propiedades y los nombres simulados coincidirán.
- Inyección directa en el campo - igual que para la inyección basada en un fijador
Tenga en cuenta que no se informa de ninguna falla en caso de que alguna de las estrategias mencionadas fallara.
Consulte la última @InjectMocks
de @InjectMocks
para obtener información más detallada sobre este mecanismo en la última versión de Mockito.
Instalación y configuración
Instalación
La forma preferida de instalar Mockito es declarar una dependencia de mockito-core
con un sistema de compilación de elección. A partir del 22 de julio de 2016, la última versión no beta es 1.10.19, pero se recomienda migrar a la versión 2.x.
Maven
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
Gradle
repositories { jcenter() }
dependencies { testCompile "org.mockito:mockito-core:1.+" }
También hay mockito-all
que contiene Hamcrest y Objenesis además de Mockito. Se entrega a través de Maven principalmente para usuarios de hormigas, pero la distribución se ha suspendido en Mockito 2.x.
Importar
La mayoría de las instalaciones de Mockito son métodos estáticos de org.mockito.Mockito
. Por lo tanto, Mockito se puede importar estáticamente en una clase de esta manera:
import static org.mockito.Mockito.*;
El punto de entrada de la documentación se encuentra en el javadoc de esta clase.
Se burlan de algunos métodos en un objeto
Solo algunos métodos de un objeto se pueden burlar usando el spy()
de mockito.
Por ejemplo, imagine que la clase de método requiere algún servicio web para funcionar.
public class UserManager {
List<User> users;
public UserManager() {
user = new LinkedLisk<User>();
}
public void addUser(User user) {
if (isValid(user)) {
user.add(user);
} else {
throw new NotValidUserException();
}
}
protected boolean isValid(User user) {
//some online web service to check if user is valid
}
public int numberOfUsers() {
return users.size();
}
}
addUser
método addUser
se debe probar para realizar una prueba útil para UserManager
. Sin embargo, aquí se encuentra una dependencia, isValid
requiere un servicio web externo que no está contenido en nuestro código. Entonces, esta dependencia externa debe ser neutralizada.
En este caso, si solo se burla de isValid
, podrá probar el resto de los métodos de UserManager
.
@Test
public void testAddUser() {
User user = mock(User.class);
UserManager manager = spy(new UserManager());
//it forces to manager.isValid to return true
doReturn(true).when(manager).isValid(anyObject());
manager.addUser(user);
assertTrue(manager.numberOfUsers(), 1);
}
Puede comprobar fácilmente el escenario donde el user
no es válido.
@Test(expectedExceptions = NotValidUserException.class)
public void testNotValidAddUser() {
User user = mock(User.class);
UserManager manager = spy(new UserManager());
//it forces to manager.isValid to return false
doReturn(false).when(manager).isValid(anyObject());
manager.addUser(user);
}
Prueba de Mockito mínimo simple
Este ejemplo muestra una prueba de Mockito mínima usando una ArrayList
simulada:
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
import java.util.ArrayList;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest
{
@Mock
ArrayList<String> listMock;
@Test
public void testAppend() {
// configure the mock to return "foobar" whenever "get()"
// is called on "listMock" with an int value as parameter
doReturn("foobar").when(listMock).get(anyInt());
String result = listMock.get(0);
assertEquals("foobar", result);
}
}
Verificando argumentos con ArgumentCaptor
Para validar argumentos a métodos llamados en un simulacro, use la clase ArgumentCaptor
. Esto le permitirá extraer los argumentos en su método de prueba y realizar aseveraciones en ellos.
Este ejemplo prueba un método que actualiza el nombre de un usuario con un ID determinado. El método carga al usuario, actualiza el atributo de name
con el valor dado y lo guarda después. La prueba desea verificar que el argumento pasado al método de save
es un objeto User
con la ID y el nombre correctos.
// This is mocked in the test
interface UserDao {
void save(User user);
}
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@Mock
UserDao userDao;
@Test
public void testSetNameForUser() {
UserService serviceUnderTest = new UserService(userDao);
serviceUnderTest.setNameForUser(1L, "John");
ArgumentCaptor<User> userArgumentCaptor = ArgumentCaptor.forClass(User.class);
verify(userDao).save(userArgumentCaptor.capture());
User savedUser = userArgumentCaptor.getValue();
assertTrue(savedUser.getId() == 1);
assertTrue(savedUser.getName().equals("John"));
}
}
Verificación de argumentos con ArgumentMatcher
Mockito proporciona una interfaz Matcher<T>
junto con una clase abstracta ArgumentMatcher<T>
para verificar los argumentos. Utiliza un enfoque diferente para el mismo caso de uso que el ArgumentCaptor
. Además, el ArgumentMatcher también se puede utilizar para burlarse. Ambos casos de uso hacen uso del método Mockito.argThat()
que proporciona un código de prueba razonablemente legible.
verify(someMock).someMethod(Mockito.argThat(new ArgumentMatcher<String>() {
@Override
public boolean matches(Object o) {
return o instanceof String && !((String)o).isEmpty();
}
});
Desde los JavaDocs de ArgumentMatcher:
Advertencia: Sea razonable con el uso de la coincidencia de argumentos complicados, especialmente los emparejadores de argumentos personalizados, ya que puede hacer que la prueba sea menos legible. A veces es mejor implementar equals () para los argumentos que se pasan a simulacros (Mockito, naturalmente, usa equals () para la coincidencia de argumentos). Esto puede hacer que la prueba sea más limpia.
Crea objetos burlados por mockito.
Hay dos formas de crear un objeto burlado por Mockito:
- a través de la anotación
- a través de la función simulada
A través de la anotación:
Con un corredor de prueba JUnit:
@RunWith(MockitoJUnitRunner.class)
public class FooTest {
@Mock
private Bar barMock;
// ...
}
También puede usar la función JUnit @Rule
, que proporciona la misma funcionalidad que MockitoJUnitRunner
, pero no necesita un @RunWith
prueba @RunWith
:
public class FooTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
@Mock
private Bar barMock;
// ...
}
Si no puede usar @RunWith
o la anotación @Rule
, también puede @Rule
"por mano":
public class FooTest {
@Mock
private Bar barMock;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
// ...
}
A través de la función simulada:
public class FooTest {
private Bar barMock = Mockito.mock(Bar.class);
// ...
}
Debido al borrado de tipo, no puede simular una clase genérica como se indicó anteriormente. Debe burlarse de la clase base y convertir explícitamente al tipo genérico correcto:
public class FooTest {
private Bar<String> genericBarMock = (Bar<String>) Mockito.mock(Bar.class);
// ...
}
Añadir comportamiento a objeto simulado
Mockito.when(mock.returnSomething()).thenReturn("my val");
mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "my val" again
mock.returnSomething(); // returns "my val" again and again and again...
Si desea un valor diferente en la segunda llamada, puede agregar el argumento de retorno deseado al método de Retorno:
Mockito.when(mock.returnSomething()).thenReturn("my val", "other val");
mock.returnSomething(); // returns "my val"
mock.returnSomething(); // returns "other val"
mock.returnSomething(); // returns "other val" again
Si llama al método sin agregar comportamiento al simulacro, devolverá nulo:
barMock.mock.returnSomethingElse(); // returns null
En caso de que el método simulado tenga parámetros, también debe declarar los valores:
Mockito.when(mock.returnSomething("param 1")).thenReturn("my val 1");
Mockito.when(mock.returnSomething("param 2")).thenReturn("my val 2");
mock.returnSomething("param 1"); // returns "my val 1"
mock.returnSomething("param 2"); // returns "my val 2"
mock.returnSomething("param 3"); // returns null
Si no te importa el valor param, puedes usar Matchers.any ():
Mockito.when(mock.returnSomething(Matchers.any())).thenReturn("p1");
mock.returnSomething("param 1"); // returns "p1"
mock.returnSomething("param other"); // returns "p1"
Para lanzar la excepción use el método thenThrow:
Mockito.when(mock.returnSomething()).thenThrow(new Exception());
mock.returnSomething(); // throws Exception
Revise los argumentos pasados a simular
Asumamos que tenemos esta clase y nos gustaría probar el método doSmth
. En este caso, queremos ver si el parámetro "val" se pasa a foo
. Objeto foo
se burla.
public class Bar {
private final Foo foo;
public Bar(final Foo foo) {
this.foo = foo;
}
public void doSmth() {
foo.bla("val");
}
}
Podemos lograr esto con ArgumentCaptor
:
@Mock
private Foo fooMock;
@InjectMocks
private Bar underTest;
@Captor
private ArgumentCaptor<String> stringCaptor;
@Test
public void should_test_smth() {
underTest.doSmth();
Mockito.verify(fooMock).bla(stringCaptor.capture());
assertThat(stringCaptor.getValue(), is("val"));
}
Verifique las llamadas de método en objeto simulado
Para verificar si se llamó a un método en un objeto Mockito.verify
puede usar el método Mockito.verify
:
Mockito.verify(someMock).bla();
En este ejemplo, afirmamos que el método bla
fue llamado en el objeto simulado someMock
.
También puede verificar si un método fue llamado con ciertos parámetros:
Mockito.verify(someMock).bla("param 1");
Si desea verificar que no se haya llamado a un método, puede pasar un parámetro adicional de VerificationMode
para verify
:
Mockito.verify(someMock, Mockito.times(0)).bla();
Esto también funciona si desea comprobar que este método fue llamado más de una vez (en este caso, verificamos que el método bla
fue llamado 23 veces):
Mockito.verify(someMock, Mockito.times(23)).bla();
Estos son más ejemplos para el parámetro VerificationMode
, que proporcionan un mayor control sobre el número de veces que se debe llamar un método:
Mockito.verify(someMock, Mockito.never()).bla(); // same as Mockito.times(0)
Mockito.verify(someMock, Mockito.atLeast(3)).bla(); // min 3 calls
Mockito.verify(someMock, Mockito.atLeastOnce()).bla(); // same as Mockito.atLeast(1)
Mockito.verify(someMock, Mockito.atMost(3)).bla(); // max 3 calls
Métodos de vacío
void
métodos void
pueden eliminarse utilizando la familia de métodos doThrow () , doAnswer () , doNothing () , doCallRealMethod () .
Runnable mock = mock(Runnable.class);
doThrow(new UnsupportedOperationException()).when(mock).run();
mock.run(); // throws the UnsupportedOperationException
Tenga en cuenta que los métodos de void
no se pueden apagar when(..)
hacen que al compilador no le gusten los métodos de void
como argumento.