Mockito + PowerMock – mockowanie prywatnej metody singletonu
Dziś krótko. Sytuacja jest prosta, mamy do zamockowania prywatną metodę w singletonie. W uproszczeniu może wyglądać to tak:
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton get() { return INSTANCE; } public String doPublic(String in) { return doPrivate(in); } private String doPrivate(String in) { return "fromPrivate" + in; } }
Chcemy zamockować metodę doPrivate, aby zwracała zupełnie coś innego. Wykorzystamy do tego Mockito w połączeniu z PowerMock oraz oczywiście JUnit. Zależności dla mavena:
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.10</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>1.4.12</version> <scope>test</scope> </dependency>
Mockito jest w zależnościach PowerMock, więc zostanie zaciągnięte automatycznie.
Poniżej rozwiązanie naszego problemu:
import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({Singleton.class}) public class SingletonTest { private Singleton singleton; @Before public void prepareTest() throws Exception { singleton = PowerMockito.spy(Singleton.get()); PowerMockito.doReturn("fromTest").when(singleton, "doPrivate", Mockito.anyString()); } @Test public void test() throws Exception { String res = singleton.doPublic("qwerty"); Assert.assertEquals("fromTest", res); } }
Ważny jest sposób w jaki tworzymy mock’a naszej klasy, patrz linia 18 powyżej. Pamiętać należy również, aby wszystkie klasy, które będą mock’owane przez PowerMock wskazać w adnotacji @PrepareForTest.
Kombinacja JUnit + Mockito + PowerMock nadaje się świetnie do pisania testów jednostkowych.
Wszystko fajnie, ale po co testować metody prywatne? Widzę dwie sytuacje: pierwsza, ze dostajemy kod spadkowy w którym (nie wiem w sumie po co) chcemy przetestować coś prywatnego, a druga opcja kiedy realizujemy nową rzeźbę. W tym ostatnim – według mnie – moment, w którym stwierdzamy, że potrzebujemy przetestować coś prywatnego świadczy o tym, że coś bardzo mocno zamieszaliśmy i trzeba to szybko wyprostować ! Metody prywatne stanowią szczegóły implementacji danej klasy, testować powinniśmy natomiast jej interfejs (czyli to co publiczne). Ponadto jestem przekonania, że testy powinny być właśnie odporne na zmiany w implementacji zwłaszcza te w częściach prywatnych – jakość testów jednostkowych.
Przy okazji. Do assercji polecam fest-assert 🙂