2015-02-24 8 views
11

Ho utilizzato Java 8 per alcuni mesi e ho iniziato a utilizzare le espressioni Lambda, che sono molto convenienti in alcuni casi. Tuttavia, mi capita spesso di incontrare alcuni problemi per testare il codice che utilizza un Lambda.Codice di test unitario con Java 8 Lambdas

prendere come esempio il seguente pseudo-codice:

private Bar bar; 

public void method(int foo){ 
    bar.useLambda(baz -> baz.setFoo(foo)); 
} 

Un approccio potrebbe essere quello di verificare solo la chiamata sulla barra

verify(bar).useLambda(Matchers.<Consumer<Baz>>.any()); 

Ma, così facendo, non prova Il codice di Lambda.

Si noti inoltre che non sono in grado di sostituire il Lambda con un riferimento metodo e l'uso metodo:

bar.useLambda(This::setFooOnBaz); 

Perché io non avrò il foo su quel metodo. O almeno questo è quello che penso.

Hai già avuto questo problema? Come posso testare o refactoring il mio codice per testarlo correttamente?


Modifica

Da quello che sto codifica è un test di unità, non voglio creare un'istanza bar, e userò un mock invece. Quindi non sarò in grado di verificare solo la chiamata baz.setFoo.

risposta

14

Non è possibile testare direttamente un lambda, poiché non ha un nome. Non c'è modo di chiamarlo a meno che tu non abbia un riferimento ad esso.

L'alternativa normale è di rifattorizzare il lambda in un metodo denominato e utilizzare un riferimento al metodo dal codice del prodotto e chiamare il metodo in base al nome del codice di test. Come si nota, questo caso non può essere refactored in questo modo perché cattura foo e l'unica cosa che può essere catturata da un riferimento al metodo è il ricevitore.

Ma lo answer from yshavit tocca un punto importante sulla necessità o meno di testare i metodi privati. Un lambda può certamente essere considerato un metodo privato.

C'è un punto più grande anche qui. Uno dei principi del test unitario è che non è necessario testare l'unità con qualsiasi cosa che sia too simple to break. Questo si allinea bene con il caso ideale per lambda, che è un'espressione così semplice che è ovviamente corretta. (Almeno, questo è quello che considero ideale.) Si consideri l'esempio:

baz -> baz.setFoo(foo) 

C'è qualche dubbio che questa espressione lambda, quando ha consegnato un riferimento Baz, chiamerà il suo metodo setFoo e passarlo foo come argomento? Forse è così semplice che non ha bisogno di essere testato unitamente.

D'altro canto, questo è solo un esempio e forse il lambda effettivo che si desidera testare è notevolmente più complicato. Ho visto il codice che utilizza lambda di grandi dimensioni, nidificate e multilinea. Vedere this answer e la sua domanda e altre risposte, per esempio. Tali lambda sono davvero difficili da debellare e testare. Se il codice nel lambda è abbastanza complesso da giustificare il test, forse quel codice dovrebbe essere rifattorizzato dal lambda, in modo che possa essere testato usando le solite tecniche.

+0

Avevi ragione quando hai detto che il mio lambda attuale è un po 'più complicato di un set, ma è ancora abbastanza semplice da considerare che non si romperà. Dopotutto, forse sto solo cercando di testare il 100% del codice, e questo è il problema alla radice. Grazie! – Fdiazreal

11

Trattare i lambda come se fosse un metodo privato; non testarlo separatamente, ma piuttosto testare l'effetto che ha. Nel tuo caso, invocare method(foo) dovrebbe causare lo bar.setFoo, quindi chiamare method(foo) e quindi verificare bar.getFoo().

+1

Concordato, non testare l'unità lambda; unità testare il risultato. – whitfin

+0

Come verificherai 'baz.setFoo' senza istanziare la barra, quindi usando la barra come simulazione? – Fdiazreal

+3

@Fdiazreal: poiché il codice sta delegando a 'Bar', non c'è motivo di testare questa azione" senza istanziare la barra ". Naturalmente, puoi testare 'baz.setFoo (foo)' anche da solo. Dato che 'setFoo' è un metodo normale, non dovresti avere problemi a provarlo, semplicemente dimenticarti del lambda. – Holger