2013-02-28 9 views
5

sto cercando di scrivere uno unit test per questo:Come posso risolvere correttamente una risorsa chiudibile automaticamente?

try (final DatagramChannel channel = helper.createChannel()) { 

... 

} 

Nella mia prova, ho deridere il helper (utilizzando Mockito), e dire helper.createChannel() per restituire un canale deriso.

Questo test ha esito negativo con

java.lang.NullPointerException 
at java.nio.channels.spi.AbstractInterruptibleChannel.close(AbstractInterruptibleChannel.java:111) 

ho capito che la struttura try-with-risorse in Java chiama il metodo close() nella DatagramChannel all'uscita del blocco try, ma non dovrebbe la close() viene chiamato il metodo nel DatagramChannel deriso?

Il debugger mi dice che il closeLock in AbstractInterruptibleChannel è nullo.

Devo creare una sottoclasse di DatagramChannel, sovrascrivere il metodo close() e quindi prendere in giro la mia sottoclasse? Oppure, sto facendo qualcosa di sbagliato in un modo più profondo (l'aiutante deride restituisce una finta)?

saluti, Fredrik Israelsson

codice di prova, su richiesta:

@Mock 
private InetAddress peerAddress; 
@Mock 
private UDPChannelHelper helper; 
@Mock 
private DatagramChannel channel; 

private UDPTransportImpl transport; 

@Before 
public void setUp() throws Exception { 
    MockitoAnnotations.initMocks(this); 
    when(helper.createChannel()).thenReturn(channel); 
    transport = new UDPTransportImpl(peerAddress, 0, helper); 
} 

@Test 
public void testNormalSubmit() throws Exception { 
    transport.submit("Hello"); 
} 

Come potete vedere, non mi specificare qualsiasi comportamento per channel.close(). Sono convinto che non dovrei farlo, perché close() restituisce il nulla.

+0

Puoi mostrare il tuo codice dove stai prendendo in giro questi? Inoltre hai eseguito il debug e confermato che a) l'helper è in realtà un simulatore, b) helper.createChannel() restituisce anche un oggetto deriso? – cowls

+0

Aggiunto il codice di test e sì, nel debugger, sia helper che channel sono del tipo BlaBlaBla $$ EnhancerByMockitoWithCGLIB. –

+1

Questa pagina: http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#doNothing%28%29 afferma esplicitamente "che i metodi di annullamento su mock non eseguono nulla di default!". Quindi possiamo solo supporre che il blocco finally non sia in esecuzione su una simulazione. Il tuo codice mi sembra buono, quindi non sono sicuro del motivo per cui ... non dovresti comunque aver bisogno di sottoclassi tu stesso. – cowls

risposta

6

Stai prendendo in giro una vera classe DatagramChannel, che si estende AbstractInterruptibleChannel. Tuttavia lo AbstractInterruptibleChannel.close è definitivo e Mockito al momento non può simulare il codice finale. Questo spiega perché hai un NPE nel codice.

Devo ricordarti che è comunemente accettato che i tipi di derisione che non possiedi sono una cattiva pratica. Ho visto persone farlo e hanno avuto brutte sorprese anni dopo, quando la vera implementazione era cambiata, ma il comportamento finto non lo faceva, quindi pensavano erroneamente che tutto fosse a posto quando aggiornavano la versione delle librerie.

Tuttavia, se si desidera continuare in questo modo perché si dispone di validi motivi (e ce ne sono alcuni), è possibile restituire invece una simulazione di un'interfaccia, ad esempio Channel che estende effettivamente lo Closeable. Oppure puoi utilizzare qualsiasi altra interfaccia di cui hai bisogno per interagire con quella presente in DatagramChannel. Inoltre, se hai bisogno di più di una interfaccia usa solo mock(Channel.class, withSetting().extraInterfaces(...)).

Speranza che aiuta Cheers, Brice

1

Tenendo da parte se si dovrebbe fare questo o no, un modo è possibile ovviare a questo problema è quello di "fissare" l'istanza finto AbstractInterruptibleChannel (se un FileChannel, un DatagramChannel, ecc.) Fornendo un oggetto per il campo closeLock utilizzato per sincronizzare la chiamata chiusa.

private static void fixChannelMock(AbstractInterruptibleChannel mockFileChannel) throws Exception { 
    Field closeLockField = AbstractInterruptibleChannel.class.getDeclaredField("closeLock"); 
    closeLockField.setAccessible(true); 
    closeLockField.set(mockFileChannel, new Object()); 
} 

essere preparati a dover risolvere il codice di cui sopra sulla release Java minori anche se, come l'implementazione interna di AbstractInterruptibleChannel potrebbe cambiare.

0

Ho avuto lo stesso problema e usare spy (..) invece di mock (..) ha funzionato per me.Stavo cercando di simulare un errore durante il troncamento di un file e se il mio sistema gestiva l'errore di conseguenza.

FileChannel fileChannel = spy(FileChannel.class); 
mockStatic(FileChannel.class); 
when(FileChannel.open(eq(filePath), eq(StandardOpenOption.WRITE))).thenReturn(fileChannel); 
when(fileChannel.truncate(1000L)).thenThrow(new IOException("Unable to truncate file")); 

... 

// Snippet being tested! 
fileChannel = FileChannel.open(filePath, StandardOpenOption.WRITE); 
fileChannel.truncate(1000L); // Will throw the exception! 
Problemi correlati