2010-02-16 27 views
679

¿Cómo simular métodos con el tipo de retorno nulo?Cómo hacer simulacros a métodos vacíos con mockito

Implementé un patrón Observer pero no puedo simularlo con Mockito porque no sé cómo.

Y traté de encontrar un ejemplo en Internet, pero no tuvo éxito.

Mi clase se parece

public class World { 

    List<Listener> listeners; 

    void addListener(Listener item) { 
     listeners.add(item); 
    } 

    void doAction(Action goal,Object obj) { 
     setState("i received"); 
     goal.doAction(obj); 
     setState("i finished"); 
    } 

    private string state; 
    //setter getter state 
} 

public class WorldTest implements Listener { 

    @Test public void word{ 
    World w= mock(World.class); 
    w.addListener(this); 
    ... 
    ... 

    } 
} 

interface Listener { 
    void doAction(); 
} 

El sistema no se activan con la maqueta. = ( quiero mostrar por encima de estado del sistema mencionado. Y hacer la afirmación según ellos.

+0

Mira que los métodos vacíos de burla no hacen nada por defecto! – Line

Respuesta

43

La solución del llamado problema es utilizar un spyMockito.spy(...) en lugar de un mockMockito.mock(..).

Spy nos permite hacer burlas parciales. Mockito es bueno en este asunto. Debido a que tiene una clase que no está completa, de esta manera se burla de algún lugar requerido en esta clase.

+2

Me tropecé aquí porque tenía un problema similar (también, casualmente, estaba probando una interacción Subject/Observer). Ya estoy usando un espía, pero quiero el método 'SubjectChanged' para hacer algo diferente. Podría usar 'verify (observador) .subjectChanged (subject) jus t para ver que se llamó el método. Pero, por alguna razón, prefiero anular el método. Para eso, una combinación del enfoque de Sateesh y tu respuesta aquí era el camino a seguir ... – gMale

+26

No, hacer esto no ayudará con los métodos de anulación burlona. El truco es usar uno de los cuatro métodos estáticos de Mockito enumerados en la respuesta de Sateesh. –

+0

¿y si la clase es abstracta, no puedes espiar y luego corregir? – Gurnard

876

Tome un vistazo a la Mockito API docs. A medida que el documento vinculado menciona (punto # 12), puede utilizar cualquiera de los doThrow(), doAnswer() , doNothing(), doReturn() familia de métodos de marco Mockito para burlarse de los métodos vacíos.

Por ejemplo,

Mockito.doThrow(new Exception()).when(instance).methodName(); 

o si quieres combinarlo con el comportamiento de seguimiento,

Mockito.doThrow(new Exception()).doNothing().when(instance).methodName(); 

Suponiendo que usted está buscando en burlarse de la incubadora setState(String s) en el Mundial de la clase a continuación es el código utiliza doAnswer método para burlarse de la setState.

World mockWorld = mock(World.class); 
doAnswer(new Answer<Void>() { 
    public Void answer(InvocationOnMock invocation) { 
     Object[] args = invocation.getArguments(); 
     System.out.println("called with arguments: " + Arrays.toString(args)); 
     return null; 
    } 
}).when(mockWorld).setState(anyString()); 
+7

@qualidafial: Sí, creo que la parametrización en Void sería mejor, ya que transmite mejor que no estoy interesado en el tipo de devolución. No estaba al tanto de esta construcción, gracias por señalarlo. – sateesh

+1

doThrow es el # 5 ahora (también para mí, usando doThrow esto solucionó el mensaje "tipo 'vacío' no permitido aquí", para seguidores ...) – rogerdpack

+0

@qualidafial: creo que el tipo de devolución de la llamada Answer.answer no es lo se devuelve al método original, es lo que se devuelve a la llamada a doAnswer, presumiblemente si desea hacer algo más con ese valor en la prueba. – twelve17

10

Añadiendo otra respuesta a la racimo (sin doble sentido) ...

Sí es necesario llamar al método doAnswer si no puede \ no quieren usar espionaje. Sin embargo, no necesariamente necesita hacer su propio Answer. Hay varias implementaciones predeterminadas. Notablemente, CallsRealMethods.

En la práctica, se ve algo como esto:

doAnswer(new CallsRealMethods()).when(mock) 
     .voidMethod(any(SomeParamClass.class)); 

O:

doAnswer(Answers.CALLS_REAL_METHODS.get()).when(mock) 
     .voidMethod(any(SomeParamClass.class)); 
75

creo que he encontrado una respuesta simple a esa pregunta, para llamar al método real para un solo método (incluso si no tiene un retorno void) se puede hacer esto:

Mockito.doCallRealMethod().when(<objectInstance>).<method>(); 
<objectInstance>.<method>(); 

O bien, puede llamar al método real para todos los métodos de esa clase, haciendo esto:

<Object> <objectInstance> = mock(<Object>.class, Mockito.CALLS_REAL_METHODS); 
+9

Esta es la verdadera respuesta aquí. El método espía() funciona bien, pero generalmente está reservado para cuando quiere que el objeto haga la mayoría de las cosas normalmente. – biggusjimmus

+1

¿Qué significa esto? ¿Estás realmente llamando a los métodos? Realmente no he usado mockito antes. – obesechicken13

+0

Sí, el simulacro llamará a los métodos reales. Si usa @Mock, puede especificar lo mismo con: @Mock (answer = Answers.CALLS_REAL_METHODS) para obtener los mismos resultados. – Ale

4

Creo que sus problemas se deben a la estructura de su prueba.Me ha resultado difícil mezclar burlas con el método tradicional de implementación de interfaces en la clase de prueba (como lo ha hecho aquí).

Si implementa el oyente como un simulacro, puede verificar la interacción.

Listener listener = mock(Listener.class); 
w.addListener(listener); 
world.doAction(..); 
verify(listener).doAction(); 

Esto debería convencerte de que el 'Mundo' está haciendo lo correcto.

41

Agregando a lo @sateesh Dicho esto, cuando lo que desea es burlarse de un método de vacío con el fin de evitar que la prueba de llamar a él, se puede utilizar un Spy esta manera:

World world = new World(); 
World spy = Mockito.spy(world); 
Mockito.doNothing().when(spy).methodToMock(); 

Cuando desea ejecutar su prueba, asegúrese de llamar al método en prueba en el objeto spy y no en el objeto world. Por ejemplo:

assertEquals(0,spy.methodToTestThatShouldReturnZero()); 
19

En primer lugar: siempre se debe importar Mockito estática, de esta manera el código será mucho más fácil de leer (e intuitivo):

import static org.mockito.Mockito.*; 

por burlarse parcial y manteniendo funcionalidad original en el resto, mockito ofrece "Spy".

Se puede utilizar de la siguiente manera:

private World world = spy(World.class); 

Para eliminar un método de ser ejecutado podría utilizar algo como esto:

doNothing().when(someObject).someMethod(anyObject()); 

en hacerle un comportamiento personalizado a un uso del método "cuando "con un" thenReturn ":

doReturn("something").when(this.world).someMethod(anyObject()); 

Para obtener más ejemplos, encuentre las muestras de mockito en el d jefe.

+0

¿Qué tiene que ver la importación estática para que sea más legible? – functioncall

0

@ashley: funciona para mí

public class AssetChangeListenerImpl extends 
AbstractAssetChangeListener implements AssetChangeListener { 

    @Override 
    public void onChangeEvent(final EventMessage message) throws EventHubClientException { 
     execute(message); 
    } 
    } 
} 

public class AbstractAssetChangeListener { 
    protected void execute(final EventMessage message) throws EventHubClientException { 
    executor.execute(new PublishTask(getClient(), message)); 
} } @RunWith(MockitoJUnitRunner.class) public class AssetChangeListenerTest extends AbstractAssetChangeListenerTest { 

public void testExecute() throws EventHubClientException { 
    EventMessage message = createEventMesage(EventType.CREATE); 
    assetChangeListener.execute(message); 
    verify(assetChangeListener, times(1)).execute(message); 
} } 
12

Cómo se burlan de los métodos de vacío con Mockito - hay dos opciones:

  1. doAnswer - Si queremos que nuestro vacío burlado método para hacer algo (burlarse del comportamiento a pesar de ser nulo).
  2. doThrow - Luego está Mockito.doThrow() si desea lanzar una excepción del método de anulación de burla.

A continuación se muestra un ejemplo de cómo usarlo (no es un uso ideal pero solo quería ilustrar el uso básico).

@Test 
public void testUpdate() { 

    doAnswer(new Answer<Void>() { 

     @Override 
     public Void answer(InvocationOnMock invocation) throws Throwable { 
      Object[] arguments = invocation.getArguments(); 
      if (arguments != null && arguments.length > 1 && arguments[0] != null && arguments[1] != null) { 

       Customer customer = (Customer) arguments[0]; 
       String email = (String) arguments[1]; 
       customer.setEmail(email); 

      } 
      return null; 
     } 
    }).when(daoMock).updateEmail(any(Customer.class), any(String.class)); 

    // calling the method under test 
    Customer customer = service.changeEmail("[email protected]", "[email protected]"); 

    //some asserts 
    assertThat(customer, is(notNullValue())); 
    assertThat(customer.getEmail(), is(equalTo("[email protected]"))); 

} 

@Test(expected = RuntimeException.class) 
public void testUpdate_throwsException() { 

    doThrow(RuntimeException.class).when(daoMock).updateEmail(any(Customer.class), any(String.class)); 

    // calling the method under test 
    Customer customer = service.changeEmail("[email protected]", "[email protected]"); 

} 
} 

Se puede encontrar más detalles sobre cómo simulacros y pruebavacío métodos con Mockito en mi post How to mock with Mockito (A comprehensive guide with examples)

+1

Gran ejemplo. Nota: En Java 8, podría ser un poco más agradable de usar una lambda en lugar en lugar de una clase annonymous:. 'doAnswer ((Respuesta ) invocación -> {// CÓDIGO }) cuando (mockInstance) .add (método());' – miwe

3

En Java 8 esto se puede hacer un poco más limpio, suponiendo que tiene una importación estática para org.mockito.Mockito.doAnswer:

doAnswer((i) -> { 
    // Do stuff with i.getArguments() here 
    return null; 
}).when(*mock*).*method*(*methodArguments*); 

El return null; es importan y sin él la compilación fallará con algunos errores bastante oscuros ya que no podrá encontrar una anulación adecuada para doAnswer.

Por ejemplo, un ExecutorService que solo se ejecuta inmediatamente cualquier Runnable pasado a execute() podrían implementarse usando:

doAnswer((i) -> { 
    ((Runnable) i.getArguments()[0]).run(); 
    return null; 
}).when(executor).execute(any()); 
Cuestiones relacionadas