2015-03-20 13 views
24

Sto provando a testare una classe di servizio, che utilizza internamente un oggetto di connessione AMQP Spring. Questo oggetto di connessione è iniettato da Spring. Tuttavia, non voglio che il mio test unitario comunichi effettivamente con il broker AMQP, quindi sto usando Mockito per iniettare una simulazione dell'oggetto di connessione.In che modo Mockito può acquisire gli strumenti passati ai metodi di un oggetto fittizio iniettato?

/** 
* The real service class being tested. Has an injected dependency. 
*/ 
public class UserService { 

    @Autowired 
    private AmqpTemplate amqpTemplate; 

    public final String doSomething(final String inputString) { 
     final String requestId = UUID.randomUUID().toString(); 
     final Message message = ...; 
     amqpTemplate.send(requestId, message); 
     return requestId; 
    } 
} 

/** 
* Unit test 
*/ 
public class UserServiceTest { 

    /** This is the class whose real code I want to test */ 
    @InjectMocks 
    private UserService userService; 

    /** This is a dependency of the real class, that I wish to override with a mock */ 
    @Mock 
    private AmqpTemplate amqpTemplateMock; 

    @Before 
    public void initMocks() { 
     MockitoAnnotations.initMocks(this); 
    } 

    @Test 
    public void testDoSomething() { 
     doNothing().when(amqpTemplateMock).send(anyString(), any(Message.class)); 

     // Call the real service class method, which internally will make 
     // use of the mock (I've verified that this works right). 
     userService.doSomething(...); 

     // Okay, now I need to verify that UUID string returned by 
     // "userService.doSomething(...) matches the argument that method 
     // internally passed to "amqpTemplateMock.send(...)". Up here 
     // at the unit test level, how can I capture the arguments passed 
     // to that inject mock for comparison? 
     // 
     // Since the value being compared is a UUID string created 
     // internally within "userService", I cannot just verify against 
     // a fixed expected value. The UUID will by definition always be 
     // unique. 
    } 
} 

I commenti in questo esempio di codice si spera siano disposti chiaramente. Quando Mockito inietta una dipendenza fittizia in una classe reale, e i test unitari sulla classe reale provocano le chiamate alla simulazione, come puoi recuperare in seguito gli argomenti esatti che sono stati trasmessi al mock iniettato?

risposta

43

Utilizzare uno o più ArgumentCaptor s.

Non è chiaro quali sono i tuoi tipi qui, ma comunque. Supponiamo di avere un modello che ha un metodo di doSomething() prendendo un Foo come argomento, allora si esegue questa operazione:

final ArgumentCaptor<Foo> captor = ArgumentCaptor.forClass(Foo.class); 

verify(mock).doSomething(captor.capture()); 

final Foo argument = captor.getValue(); 

// Test the argument 

Inoltre, sembra che il metodo restituisce void e non vogliono fare nulla. Basta scrivere questo:

doNothing().when(theMock).doSomething(any()); 
+0

Grazie per la punta su 'doAnswer()' 'vs. DoNothing()', mi hanno ripulito il codice di esempio sopra di conseguenza. Tuttavia, non sono sicuro se mi manchi qualcosa, o se la tua risposta è ancora un livello rimosso dal mio problema. Quando comincio chiamando 'verify()' sul mio oggetto mock PRIMA che abbia chiamato la vera classe che usa internamente il mock, allora ottengo il messaggio di eccezione, "In realtà, non c'erano interazioni nulle con questo mock". L'esecuzione fallisce sulla riga 'verify()' e non arriva mai nemmeno al codice reale che sto provando a testare. –

+0

Nel tuo esempio, stai cercando di passare un argomento al tuo oggetto mock direttamente dal test dell'unità? Non sto ... Sto cercando di catturare un argomento che è passato da uno strato intermedio tra il test unitario e l'oggetto simulato. La finta viene iniettata in questo intermediario. –

+3

Ahh! Il problema che non è chiaro dal tuo snippet è che la chiamata 'verify()' sul mock, così come la chiamata 'getValue()' sul captor, entrambi vengono DOPO l'invocazione del metodo sull'oggetto reale effettivamente sotto test. Ho erroneamente chiamato 'verify()' prima dell'invocazione del metodo dell'oggetto reale. –

6

È possibile agganciare doAnswer() allo stub del metodo send() su amqpTemplateMock e quindi acquisire gli argomenti di invocazione di AmqpTemplate.send().

fare la prima riga del tuo testDoSomething() essere questo

Mockito.doAnswer(new Answer<Void>() { 
      @Override 
      public Void answer(final InvocationOnMock invocation) { 
      final Object[] args = invocation.getArguments(); 
      System.out.println("UUID=" + args[0]); // do your assertions here 
      return null; 
      } 
    }).when(amqpTemplateMock).send(Matchers.anyString(), Matchers.anyObject()); 

mettendo tutto insieme, il test diventa

import org.junit.Before; 
import org.junit.Test; 
import org.mockito.InjectMocks; 
import org.mockito.Matchers; 
import org.mockito.Mock; 
import org.mockito.Mockito; 
import org.mockito.MockitoAnnotations; 
import org.mockito.invocation.InvocationOnMock; 
import org.mockito.stubbing.Answer; 

public class UserServiceTest { 

    /** This is the class whose real code I want to test */ 
    @InjectMocks 
    private UserService userService; 

    /** This is a dependency of the real class, that I wish to override with a mock */ 
    @Mock 
    private AmqpTemplate amqpTemplateMock; 

    @Before 
    public void initMocks() { 
    MockitoAnnotations.initMocks(this); 
    } 

    @Test 
    public void testDoSomething() throws Exception { 
    Mockito.doAnswer(new Answer<Void>() { 
     @Override 
     public Void answer(final InvocationOnMock invocation) { 
     final Object[] args = invocation.getArguments(); 
     System.out.println("UUID=" + args[0]); // do your assertions here 
     return null; 
     } 
    }).when(amqpTemplateMock).send(Matchers.anyString(), Matchers.anyObject()); 
    userService.doSomething(Long.toString(System.currentTimeMillis())); 
    } 
} 

Questo dà uscita

UUID = 8e276a73-12fa- 4a7e-a7cc-488d1ce0291f

Ho trovato questo leggendo questo post, How to make mock to void methods with mockito

Problemi correlati