In einfachen Worten erlaubt mir ein Mockito Spy sowohl Methoden einer Klasse ausführen zu lassen, als auch andere Methoden der Klasse zu mocken.

Stellen wir uns ein simples Beispiel einer Klasse vor, die zwei Methoden besitzt. Eine Methode wird implementiert und die andere Methode geerbt.

public class BaseService {

    private String myName = "I'm the base service...";;

    private void setMyName(String myName) {
        this.myName = myName;
    }

    public String getMyName() {
        return myName;
    }
}

public class MyService extends BaseService {

    public String doAnything() {
        return "Done: " + getMyName();
    }
}

Zufälligerweise habe ich hier den Fall konstruiert, dass man auf direkten (ohne Reflection) Wegen nicht an die Eigenschaft myName kommt.

Man könnte nun einen Test schreiben, bei dem die geerbte Methode mitausgeführt wird oder aber die Durchführung dieser Methode mocken:

...
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
...
@Test
public void testMyServiceWithCallingBaseFunction() throws Exception {
    MyService myService = new MyService();
    assertThat(myService.doAnything(),
       CoreMatchers.is("Done: I'm the base service..."));
}

@Test
public void testMyServiceWithoutCallingBaseFunction() throws Exception {
    MyService myService = spy(new MyService());
    // Here I mock the call of base class
    when(myService.getMyName()).thenReturn("dummy");
    assertThat(myService.doAnything(),
       CoreMatchers.is("Done: dummy"));
}

In Zeile 14 wird der Spy anhand eines existierenden Objekts angelegt, was notwendig ist, da man ja die Methoden, die NICHT gemockt sind, auch aufrufen möchte.
In Zeile 16 wird eine Methode des Spys gemockt.

Es ist natürlich in diesem simplen Fall auch möglich mit org.springframework.test.util.ReflectionTestUtils oder org.mockito.internal.util.reflection.Whitebox zu arbeiten, aber darum ging es mir hier nicht.

Was sicherlich keinen Sinn macht, ist die zu testende Methode zu mocken, obwohl es möglich wäre, falls man mal schnell einen grünen Test braucht 🙂

Nun gibt es allerdings eine Einschränkung, die ich nicht richtig deutlich gemacht habe, weil sie meiner Konvention entspricht: Der Test muss Zugriff auf die protected Methode haben, was wiederum bedeutet, er muss im gleichen Package liegen.

Das ist sicherlich nicht immer der Fall. Insbesondere, wenn man Tests in einem externen JAR ablegt, wird es schwierig.

In diesen Fällen kann man das Whiteboxing immer noch umgehen, indem man Powermockito verwendet:

...
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
...
@RunWith(PowerMockRunner.class)
@PrepareForTest(BaseService.class)
public class PowermockitoMyServiceTest {

   @Test
   public void test() throws Exception {
      MyService myService = PowerMockito.spy(new MyService());
      PowerMockito.doReturn("dummy").when(myService, "getMyName");
      assertThat(myService.doService(), is("Done: dummy"));
   }
}

Wenn man den PowermockRunner aus Code Coverage Gründen vermeiden will, guckt man hier.

Spys like us
Markiert in:            

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.