Come deridere i metodi del vuoto con Mockito


938

Come deridere i metodi con il tipo restituito vuoto?

Ho implementato un modello di osservatore ma non posso deriderlo con Mockito perché non so come.

E ho provato a trovare un esempio su Internet ma non ci sono riuscito.

La mia classe si presenta così:

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

Il sistema non viene attivato con simulazione.

Voglio mostrare lo stato del sistema sopra menzionato. E fare affermazioni secondo loro.


6
Attenzione che i metodi nulli su mock non fanno nulla per impostazione predefinita!
Linea

1
@Line, questo è quello che stavo cercando. Sembra ovvio dopo averlo detto. Ma mette in evidenza un principio beffardo: devi solo deridere i metodi delle classi derise per i loro effetti, come un valore di ritorno o un'eccezione. Grazie!
Allenjom,

Risposte:


1145

Dai un'occhiata ai documenti API di Mockito . Mentre il documento collegato menziona (Point # 12) è possibile utilizzare una qualsiasi delle doThrow(), doAnswer(), doNothing(), doReturn()famiglia di metodi da quadro Mockito per deridere metodi vuoto.

Per esempio,

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

o se si desidera combinarlo con il comportamento di follow-up,

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

Presumendo che tu stia cercando di deridere il setter setState(String s)nella classe World di seguito è che il codice usa il doAnswermetodo per deridere il 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());

8
@qualidafial: Sì, penso che la parametrizzazione su Void sarebbe migliore in quanto trasmette meglio che non sono interessato al tipo di ritorno. Non ero a conoscenza di questo costrutto, grazie per averlo sottolineato.
Sateesh,

2
doThrow è ora # 5 (anche per me usando doThrow questo risolveva il messaggio "tipo void" non consentito qui ", per i follower ...)
rogerdpack,

@qualidafial: penso che il tipo di ritorno della chiamata Answer.answer non sia ciò che viene restituito al metodo originale, è ciò che viene restituito alla chiamata doAnswer, presumibilmente se si desidera fare qualcos'altro con quel valore nel test.
dodici,

1
:( nel tentativo di deridere la versione 16.0.1 di RateLimiter.java in guava doNothing (). when (mockLimiterReject) .setRate (100) porta alla chiamata di setRate di RateLimiter con conseguente nullpointer poiché mutex è nullo per qualche motivo una volta mockito bytecoded quindi non ha deriso il mio metodo setRate :( ma invece l'ha chiamato :(
Dean Hiller

2
@DeanHiller nota che setRate()è final, e quindi non può essere deriso. Prova invece a provare create()un'istanza che fa ciò di cui hai bisogno. Non dovrebbe essere necessario deridere RateLimiter.
dimo414,

113

Penso di aver trovato una risposta più semplice a quella domanda, per chiamare il metodo reale per un solo metodo (anche se ha un ritorno vuoto) puoi farlo:

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

In alternativa, potresti chiamare il metodo reale per tutti i metodi di quella classe, facendo questo:

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

13
Questa è la vera risposta proprio qui. Il metodo spy () funziona bene, ma generalmente è riservato quando si desidera che l'oggetto faccia quasi tutto normalmente.
biggusjimmus,

1
Cosa significa questo? Stai davvero chiamando i metodi? Non ho mai usato mockito prima.
obesechicken13

Sì, il finto chiamerà i metodi reali. Se usi @Mock puoi specificare lo stesso con: @Mock (answer = Answers.CALLS_REAL_METHODS) per ottenere gli stessi risultati.
Ale

70

Aggiungendo a ciò che ha detto @sateesh, quando si desidera solo deridere un metodo vuoto per evitare che il test lo chiami, è possibile utilizzare in Spyquesto modo:

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

Quando si desidera eseguire il test, assicurarsi di chiamare il metodo in test spysull'oggetto e non worldsull'oggetto. Per esempio:

assertEquals(0,spy.methodToTestThatShouldReturnZero());

57

La soluzione del cosiddetto problema è utilizzare un spy Mockito.spy (...) invece di un mock Mockito.mock (..) .

La spia ci consente di deridere parzialmente. Mockito è bravo in questa faccenda. Poiché hai una classe che non è completa, in questo modo deridi un posto richiesto in questa classe.


3
Mi sono imbattuto qui perché avevo un problema simile (anche, per coincidenza, mi è capitato di provare un'interazione soggetto / osservatore). Sto già usando una spia ma voglio che il metodo "SubjectChanged" faccia qualcosa di diverso. Potrei usare `ify (observer) .subjectChanged (subject) solo per vedere che il metodo è stato chiamato. Ma, per qualche motivo, preferirei di gran lunga ignorare il metodo. Per questo, una combinazione dell'approccio di Sateesh e la tua risposta qui è stata la strada da percorrere ...
gMale

32
No, farlo non aiuterà in realtà a deridere i metodi del vuoto. Il trucco è usare uno dei quattro metodi statici Mockito elencati nella risposta di Sateesh.
Dawood ibn Kareem,

2
@Gurnard per la tua domanda dai un'occhiata a questo stackoverflow.com/questions/1087339/… .
ibrahimyilmaz,

Questo dosaggio funziona davvero.
Nilotpal,

34

Prima di tutto: dovresti sempre importare mockito static, in questo modo il codice sarà molto più leggibile (e intuitivo):

import static org.mockito.Mockito.*;

Per deridere parzialmente e mantenere ancora la funzionalità originale sul resto mockito offre "Spy".

Puoi usarlo come segue:

private World world = spy(World.class);

Per eliminare un metodo dall'esecuzione potresti usare qualcosa del genere:

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

per dare un comportamento personalizzato a un metodo usa "when" con un "thenReturn":

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

Per ulteriori esempi, consultare gli eccellenti esempi di mockito nel documento.


4
Cosa c'entra l'importazione statica con renderla più leggibile?
jsonbourne,

2
letteralmente niente.
specializt

2
Penso che sia una questione di gusti, mi piace solo che la frase assomigli (quasi) a una frase inglese, e Class.methodname (). Qualcosa () vs. methodname () .qualcosa è meno fluido da leggere.
fl0w,

1
Se lavori in gruppo, importa statico fa male al culo per chiunque altro vedere da dove proviene il metodo, dato che spesso c'è un carattere jolly nell'importazione.
LowKeyEnergy,

è corretto, tuttavia per il codice di test e Mockito questo dovrebbe essere ovvio e non un problema.
fl0w,

27

Come deridere i metodi vuoti con mockito: ci sono due opzioni:

  1. doAnswer - Se vogliamo che il nostro metodo del vuoto deriso faccia qualcosa (deridiamo il comportamento nonostante sia vuoto).
  2. doThrow- Quindi c'è Mockito.doThrow()se vuoi lanciare un'eccezione dal metodo del vuoto deriso.

Di seguito è riportato un esempio di come utilizzarlo (non un caso d'uso ideale ma volevo solo illustrare l'utilizzo di base).

@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("old@test.com", "new@test.com");

    //some asserts
    assertThat(customer, is(notNullValue()));
    assertThat(customer.getEmail(), is(equalTo("new@test.com")));

}

@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("old@test.com", "new@test.com");

}
}

Potresti trovare maggiori dettagli su come deridere e testare i metodi del vuoto con Mockito nel mio post Come deridere con Mockito (Una guida completa con esempi)


1
Ottimo esempio Nota: in java 8, potrebbe essere un po 'più bello usare un lambda invece di una classe anonima:' doAnswer ((Answer <Void>) invocation -> {// CODE}). When (mockInstance) .add (method ());'
Devo il

16

Aggiunta di un'altra risposta al gruppo (nessun gioco di parole previsto) ...

Devi chiamare il metodo doAnswer se non puoi \ non vuoi usare la spia. Tuttavia, non è necessario necessariamente inviare la tua risposta . Esistono diverse implementazioni predefinite. In particolare, CallsRealMethods .

In pratica, sembra qualcosa del genere:

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

O:

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

16

In Java 8 questo può essere reso un po 'più pulito, supponendo che tu abbia un'importazione statica per org.mockito.Mockito.doAnswer:

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

Il return null;è importante e senza di essa la compilazione fallirà con alcuni errori piuttosto oscuri in quanto non sarà in grado di trovare una sostituzione adatto per doAnswer.

Ad esempio un ExecutorServiceche esegue immediatamente qualsiasi Runnablepassaggio passato execute()potrebbe essere implementato usando:

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

2
In una riga: Mockito.doAnswer ((i) -> null) .when (istanza) .method (any ());
Akshay Thorve,

@AkshayThorve Non funziona quando in realtà vuoi fare cose con me.
Tim B

7

Penso che i tuoi problemi siano dovuti alla tua struttura di test. Ho trovato difficile mescolare il deridere con il metodo tradizionale di implementazione delle interfacce nella classe di test (come hai fatto qui).

Se si implementa l'ascoltatore come Mock, è possibile verificare l'interazione.

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

Questo dovrebbe soddisfarti che il "mondo" sta facendo la cosa giusta.


0

Utilizzando Mockito.doThrow come in:

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

puoi provare questo bell'esempio:

public void testCloseStreamOnException() throws Exception {
    OutputStream outputStream = Mockito.mock(OutputStream.class);
    IFileOutputStream ifos = new IFileOutputStream(outputStream);
    Mockito.doThrow(new IOException("Dummy Exception")).when(outputStream).flush();
    try {
      ifos.close();
      fail("IOException is not thrown");
    } catch (IOException ioe) {
      assertEquals("Dummy Exception", ioe.getMessage());
    }
    Mockito.verify(outputStream).close();
  }

Fonte: http://apisonar.com/java-examples/org.mockito.Mockito.doThrow.html#Example-19

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.