2011-12-14 15 views
19

Ho un costruttore:Come prendere in giro un costruttore con Mockito

class Builder{ 
    private String name; 
    private String address; 
    public Builder setName(String name){ 
     this.name = name; 
     return this; 
    } 
    public Builder setAddress(String address){ 
     this.address = address; 
     return this; 
    } 

} 

Mocking il costruttore in Mockito volontà mi dà nullo per ogni metodo. Quindi c'è un modo semplice per far sì che il builder si riporti automaticamente su ogni chiamata di funzione, senza prendere in giro tutte le funzioni stesse usando when().thenReturn.

+2

Hai davvero bisogno di deridere questo? Non sembra il tipo di dipendenza che vale la pena di deridere. Sembra un tipo "di dati" di classe, piuttosto che un tipo di "servizio" di classe. Raramente trovo utile creare classi in cui non c'è un comportamento reale. –

+1

È solo un esempio, il vero costruttore è un po 'più complesso e non è necessario testare in questo caso. –

+1

Puoi separarli in modo che tu * abbia * ottenuto un "muto costruttore" (che non ha bisogno di deridere) e quindi la parte di servizio che * ha * bisogno di deridere? –

risposta

8

È possibile utilizzare RETURN_DEEP_STUBS per simulare un'API concatenata.

Se si conosce l'esatto ordine sarà chiamato il costruttore, ecco un esempio di come si dovrebbe utilizzare:

Builder b = Mockito.mock(Builder.class, RETURNS_DEEP_STUBS); 
when(b.setName("a name").setAddress("an address")).thenReturn(b); 
assert b.setName("a name").setAddress("an address") == b; // this passes 

Purtroppo questo non vi darà un modo generico di beffardo "tutti i vari costruttore metodi "in modo che restituiscano sempre questo, vedere l'altra risposta è necessario.

+2

Inoltre, se si ottiene che il deep stub ordina "errato" e si esegue il cast del risultato, verranno visualizzati alcuni messaggi dall'aspetto strano, ad esempio 'java.lang.ClassCastException: org.mockito.internal.creation.jmock.ClassImposterizer $ ClassWithSuperclassToWorkAroundCglibBug $$ EnhancerByMockitoWithCGLIB $$ 851828bd non può essere lanciato su ... ' – rogerdpack

34

Il problema con l'utilizzo di RETURN_DEEP_STUBS è che si otterrà un falso diverso ogni volta che si chiama un metodo. Dalla tua domanda penso che tu voglia utilizzare una risposta predefinita che restituisca effettivamente il mock su cui è stata chiamata, per ogni metodo che ha il giusto tipo di ritorno. Questo potrebbe apparire come il seguente. Nota che non ho ancora provato questo, quindi potrebbe contenere errori di battitura, ma spero che l'intenzione sia chiara in ogni caso.

import static org.mockito.Mockito.RETURNS_DEFAULTS; 
import org.mockito.invocation.InvocationOnMock; 
import org.mockito.stubbing.Answer; 

public class SelfReturningAnswer implements Answer<Object>{ 

    public Object answer(InvocationOnMock invocation) throws Throwable { 
     Object mock = invocation.getMock(); 
     if(invocation.getMethod().getReturnType().isInstance(mock)){ 
      return mock; 
     } 
     else{ 
      return RETURNS_DEFAULTS.answer(invocation); 
     } 
    } 
} 

Quindi, quando si crea il proprio mock, specificare questa come risposta predefinita. Questo renderà il tuo finto ritorno se stesso da ogni metodo che può; ma si comporterà come un normale finto quando chiami un metodo il cui tipo di ritorno è sbagliato per la simulazione.

Crea il tuo finto come questo

Builder mockBuilder = mock(Builder.class, new SelfReturningAnswer()); 

o creare una costante per questa classe e scrivere qualcosa di simile

@Mock(answer = SELF_RETURNING) private Builder mockBuilder; 

Speranza che aiuta.

+0

Sei sicuro. Uso'verify' sul mio profondo finto striptato e supera i test. Quindi deve restituire la stessa istanza ogni volta che utilizzo un metodo builder. –

+1

Sì, ne sono assolutamente sicuro.Ho scritto un test per la tua classe Builder, come sopra, dove ho fatto finta con RETURNS_DEEP_STUBS, poi ho chiamato setAddress e setName. Il mio test ha affermato che i due mock restituiti dalle due chiamate al metodo erano diversi. Il mio test è passato. –

+0

Il mio test è su http://pastebin.com/JNPrn4ng –

12

A partire da Mockito 2.0 (beta), esiste una nuova risposta predefinita per RETURNS_SELF che si comporta in modo quasi identico a David Wallace's answer. Esempio dalla documentazione Mockito:

@Test 
public void use_full_builder_with_terminating_method() { 
    HttpBuilder builder = mock(HttpBuilder.class, RETURNS_SELF); 
    HttpRequesterWithHeaders requester = new HttpRequesterWithHeaders(builder); 
    String response = "StatusCode: 200"; 

    when(builder.request()).thenReturn(response); 

    assertThat(requester.request("URI")).isEqualTo(response); 
} 

Nota che appare sia sulla classe Mockito e sul Answers enum, così è anche compatibile con @Mock(answer = RETURNS_SELF) sintassi.

Problemi correlati