2013-02-28 11 views
5

Próbuję napisać test jednostkowy do tego:Jak mogę sfałszować zasoby automatycznie zamykane?

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

... 

} 

W moim teście, ja drwić pomocnika (używając Mockito) i powiedzieć helper.createChannel(), aby powrócić do szydzili kanał.

Ten test nie powiedzie się z

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

Rozumiem, że try-with-zasobów obiekt w Javie wywołuje metodę close() w DatagramChannel po wyjściu z bloku try, ale nie powinny ścisłe() wzywana jest metoda w szyderczym DatagramChannel?

Debugger mówi mi, że closeLock w AbstractInterruptibleChannel ma wartość null.

Czy należy podklasować DatagramChannel, zastąpić metodę close() w nim, a następnie kpić z mojej podklasy? Czy robię coś złego w bardziej głęboki sposób (pomocna sztuczka zwraca sztuczkę)?

Pozdrawiam, Fredrik Israelsson

kod testowy, na wniosek:

@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"); 
} 

Jak widać, nie określił żadnych zachowanie dla channel.close(). Jestem przekonany, że nie powinienem tego robić, ponieważ close() zwraca pustkę.

+0

Czy możesz pokazać swój kod tam, gdzie kpisz z nich? Również debugowałeś i potwierdziłeś, że a) pomocnik jest w rzeczywistości symulacją, b) helper.createChannel() zwraca również wyszydzany obiekt? – cowls

+0

Dodano kod testowy, i tak, w debugerze, zarówno helper, jak i kanał są typu BlaBlaBla $$ EnhancerByMockitoWithCGLIB. –

+1

Ta strona: http://mockito.googlecode.com/svn/tags/latest/javadoc/org/mockito/Mockito.html#doNothing%28%29 jednoznacznie stwierdza, że ​​"nieważne metody na próbach domyślnie nie robią nic!". Możemy więc tylko założyć, że blok finally nie wykonuje się na sztuczce. Twój kod wygląda dla mnie dobrze, więc nie jestem pewien, dlaczego tak jest. Z pewnością nie powinieneś jednak samodzielnie go podklasować. – cowls

Odpowiedz

6

Kpisz z prawdziwej klasy DatagramChannel, która rozszerza się o AbstractInterruptibleChannel. Jednak wersja AbstractInterruptibleChannel.close jest ostateczna, a Mockito nie może obecnie symulować ostatecznego kodu. Co wyjaśnia, dlaczego w kodzie jest NPE.

Muszę wam przypomnieć, że powszechnie przyjmuje się, że kpiny, które nie należą do was, to zła praktyka. Widziałem, jak ludzie to robią i mieli złe niespodzianki wiele lat później, kiedy prawdziwa implementacja się zmieniła, ale fałszywe zachowanie nie miało miejsca, więc błędnie sądzili, że wszystko było w porządku, gdy zaktualizowali wersję bibliotek.

Nadal, jeśli chcesz kontynuować tę drogę, bo mają ważne powody, że (a są tacy), można powrócić zamiast makiety z interfejsem, jak Channel że faktycznie rozciąga Closeable. Lub możesz użyć dowolnego innego interfejsu, z którym musisz wchodzić w interakcję, który był obecny w DatagramChannel. Jeśli potrzebujesz więcej niż jednego interfejsu, po prostu użyj mock(Channel.class, withSetting().extraInterfaces(...)).

nadzieję, że pomoże Cheers, Brice

1

Utrzymywanie czy należy to robić, czy nie na bok, jeden sposób można obejść ten problem jest „ustalania” The AbstractInterruptibleChannel mock instancji (czy w FileChannel, A DatagramChannel itp.), Udostępniając obiekt dla pola closeLock słuŜącego do synchronizowania połączenia bliskiego.

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

Bądź przygotowany na to, aby rozwiązać powyższy kod na mniejszych wersjach Java chociaż jako wewnętrznej realizacji AbstractInterruptibleChannel może się zmienić.

0

Miałem ten sam problem i za pomocą szpiega (..) zamiast udawać (..) pracował dla mnie.Próbowałem zasymulować błąd podczas obcinania pliku i jeśli mój system odpowiednio obsługiwał błąd.

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! 
Powiązane problemy