mockitoEmpezando con mockito


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

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"));
}
 

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);

    // ...
}
 

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);
    }
}
 

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);
    }  
}
 

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.

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:

  1. 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.
  2. 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.
  3. 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.

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
 

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.