Mockowanie prywatnych metod

0

Witajcie,
Natrafiłem na problem przy mockowaniu prywatnych metod. Poniżej mój unit test i stacktrace.

@RunWith(PowerMockRunner.class)
@PrepareForTest({PrettyTime.class})
public class PrettyTimeTest {

    @Test
    public void shouldReturnDays() throws Exception {
        PrettyTime s = PowerMockito.spy(new PrettyTime());
        PowerMockito.when(s, PowerMockito.method(PrettyTime.class, "formatDays")).withNoArguments().thenReturn("days");
        s.setShowMinutes(false);
        assertEquals(s.convert(), "days");
    }

}

Oczywiście metoda convert z obiektu PrettyTime, po przejściu przez kilka ifów wykonuje metodę formatDays, ta zamiast zwrócić Stringa "days", normalnie się wykonuje i wyrzuca NullPointerException ponieważ nie jest zainitializowane jedno z pól w klasie

java.lang.NullPointerException
	at pl.zaprogramowany.cms.tags.PrettyTime.formatDays(PrettyTime.java:70)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl$1.invoke(MockitoMethodInvocationControl.java:240)
	at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.performIntercept(MockitoMethodInvocationControl.java:262)
	at org.powermock.api.mockito.internal.invocation.MockitoMethodInvocationControl.invoke(MockitoMethodInvocationControl.java:190)
	at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:124)
	at org.powermock.core.MockGateway.methodCall(MockGateway.java:185)
	at pl.zaprogramowany.cms.tags.PrettyTime.formatDays(PrettyTime.java)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at org.powermock.api.mockito.internal.expectation.DefaultMethodExpectationSetup.withNoArguments(DefaultMethodExpectationSetup.java:52)
	at pl.zaprogramowany.cms.tags.PrettyTimeTest.shouldPrintDays(PrettyTimeTest.java:47)

Ktoś powie co robię źle? Znalazłem takie coś http://stackoverflow.com/questions/7803944/how-to-mock-private-method-for-testing-using-powermock ale wydaje mi się że mam wszystko zgodnie z tamtym przykładem.

1

Jeżeli nie jest to jakiś bardzo ważny legacy kod (który np. mógłbyś chcieć zrefaktoryzować, a jest na produkcji), nie powinieneś mockować prywatnych metod. Metody prywatne nie powinny istnieć z punktu widzenia testów. Skupiasz się na testowaniu publicznych metod. Radzę zastanowić się nad zmianą designu. Chyba, że pracujesz ze starym, potwornym, komercyjnym kodem i nie masz wyjścia. Jednak zwiększasz w ten sposób szansę na zabetonowanie aplikacji. Z punktu widzenia TDD w ogólności metody prywatne nie istnieją, sa po prostu efektem ubocznym refactoringu.

Testujemy interfejsy, a nie wnętrzności, które nie powinny specjalnie interesować użytkownika końcowego klasy.

0

Nie jest to żaden "stary, potworny, komercyjny" kod. Poniżej link do klasy, którą chcę testować. W takim razie powinienem dostarczać różnych danych wejściowych, tak żeby przetestować wszystkie możliwe sytuacje i robić po prostu assert na zwróconym Stringu?

http://pastebin.com/ibrvGnKi

1

Testy parametryczne to jest dobry pomysł. Pozwalają wymienić implementację np. na lepszą jakość kodu / szybszą / bardziej rozbudowaną, ale kompatybilną wstecz. A w przypadku konwertera użytkownika nie interesuje implementacja, tylko zwracane wyjście dla konkretnego wejścia. TestNG wspiera je out-of-box. Dla JUnit można to zrobić z biblioteką JUnitParams.

1 użytkowników online, w tym zalogowanych: 0, gości: 1