2012-01-31 13 views
13

Ho una classe Foo che è SUT e una classe Bar, che è il suo collaboratore. Foo chiama run(List<Object> values) su Bar con "expectedList" come argomento. Quindi, Foo aggiungerà qualche altro elemento a questo List in modo che il suo stato sarà diverso da quello che era al momento della chiamata run(). Ecco il mio caso di test.Mockito può verificare i parametri in base ai loro valori al momento della chiamata del metodo?

@Test 
public void testFoo() { 
    Bar collaborator = spy(new Bar()); 
    Foo sut = new Foo(collaborator); 
    verify(collaborator).run(expectedList); 
} 

Si noti che il collaboratore è in realtà un oggetto spia piuttosto che un mock. Questo test fallirà perché anche se run() è stato chiamato con un argomento uguale a expectedList, è stato modificato da allora e il suo valore attuale non è più uguale a expectedList. Tuttavia, questo è il modo in cui dovrebbe funzionare, quindi mi chiedo se c'è un modo per avere Mockito per memorizzare l'istantanea dei parametri quando viene chiamato un metodo e verificarli in base a questi valori piuttosto che ai valori più recenti.

risposta

11

Utilizzare un Answer controllare il valore dell'argomento quando il metodo viene chiamato. È possibile lanciare uno AssertionError all'interno dello Answer se il valore è errato oppure è possibile memorizzare il valore e fare l'affermazione alla fine.

+0

Sì, David ha ragione. A causa del modo in cui l'API di Mockito è realizzata, non è possibile verificare più chiamate con lo stesso riferimento di argomento. EasyMock può farlo perché ha una fase di attesa prima che venga eseguito il codice di produzione. Ad ogni modo invece di una 'Answer' uso un' ArgurmentCaptor' e scrivo una o più asserzioni sullo stato finale di quella lista, cioè con FEST-Assert 'assertThat (captor.getValue()). Contains (" A "," B ") .contains (" T "," U ");' – Brice

+0

@Brice - come funzionerebbe diversamente dall'approccio di Michael Wiles? –

+0

Non lo è. È solo un modo diverso per raggiungere lo scopo del test. Perché la maggior parte delle volte non è necessario controllare gli argomenti intermedi, ma solo che alcune interazioni sono avvenute e il risultato finale.Anche se devo dire se Tom aveva dei requisiti specifici, ho deciso che questo non lo avrebbe aiutato, ma in questo caso avrei evitato oggetti mutabili nel mio codice di produzione. Sembra una messaggistica tra due collaboratori e i messaggi dovrebbero essere sempre immutabili. – Brice

0

Non è possibile chiamare verify() su un oggetto che non è un mock. È questo che intendevi?

Bar collaborator = mock(Bar.class); 
Foo sut = spy(new Foo(collaborator)); 
verify(collaborator).run(expectedList); 
+0

Grazie, il codice di esempio ha avuto un errore e l'ho corretto. Questa non è la mia domanda però. Si tratta di poter verificare un argomento in base al suo valore al momento della chiamata al metodo, non quello più recente. –

-1

Perché non provare a utilizzare la cattura argomento per acquisire il valore della lista previsto quando è stato eseguito e quindi si può confrontare.

ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class); 

verify(collaborator).run(listCaptor.capture()); 

assertEquals(expectedList, argument.getValue()); 
+6

Se la tua lista modificata è la stessa istanza, quindi argomento.getValue() restituirà l'istanza 'expectedList', non una copia, quindi è essenzialmente la stessa cosa che sta facendo, non è vero? – jhericks

+0

@ Michael Wiles Grazie, ma come ha detto Jhericks, l'ArgumentCaptor cattura l'istanza List originale. –

+0

Scusa, Michael, ho downvoted la tua risposta perché la tua soluzione ha esattamente lo stesso problema del test dell'OP, come spiegato da jhericks. –

1

The answer of Dawood ibn Kareem lavorato per me, ma mi mancava un esempio, anche io uso Kotlin e Mockito-Kotlin, così la mia soluzione è simile a questo:

class Foo(var mutable: String) 

interface Bar { 
    fun run(foo: Foo) 
} 

@Test fun `validate mutable parameter at invocation`() { 
    val bar = mock<Bar>() 

    var valueAtInvocation: String? = null 
    whenever(bar.run(any())).then { 
     val foo = it.arguments.first() as Foo 
     valueAtInvocation = foo.mutable // Store mutable value as it was at the invocation 
     Unit // The answer 
    } 

    val foo = Foo(mutable = "first") 
    bar.run(foo) 
    valueAtInvocation isEqualTo "first" 

    foo.mutable = "second" 
    bar.run(foo) 
    valueAtInvocation isEqualTo "second" 
} 

valueAtInvocation rappresenterà il valore della proprietà mutabile foo.mutable all'ultimo invocazione bar.run(foo). Dovrebbe anche essere possibile fare asserzioni all'interno del blocco then {}.

Problemi correlati