2021
Mockito

Mockito

Datum: Juli 2021
Lesedauer: 3 Minuten


Unit Testing

Mockito kommt bei Verwendung der Unit Tests zum Einsatz. Doch was bedeutet das?
Unit Tests werden verwendet, um einzelne Module isoliert zu testen. Dabei ist es wichtig, dass diese Klassen möglichst keine AbhÀngigkeiten haben.

Tools

Um solche Tests durchzufĂŒhren gibt es einige Tools. Hier ein paar Beispiele:

  • JUnit
  • NUnit
  • TestNG
  • ...

Was ist Mockito?

Mockito erleichtert einem das Testen. Mithilfe des Frameworks können die Komponenten gekapselt getestet werden. Dies ermöglicht auch, einzelne Teile
eines Programms bei unvollstÀndiger Software zu verifizieren. Weitere Vorteile sind:

  • Grosse StackOverflow-Gemeinschaft – AllfĂ€llige Fragen sind höchstwahrscheinlich schon geklĂ€rt.
  • Beliebtheit – Mockito wurde 2013 als eine der Top-10-Java-Libraries angesehen.

Einbinden

In meinem Projekt verwende ich Spring Boot. Deshalb kann ich Mockito als Dependency hinzufĂŒgen.

Maven

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.11.2</version>
</dependency>

Auch mit anderen Build-Systemen sollte das Einbinden kein Problem darstellen.

Gradle

repositories { jcenter() }
dependencies { testImplementation "org.mockito:mockito-core:3.+" }

Aktivieren

Um Mockito zu verwenden, muss man dies vorerst der entsprechenden Klasse mitteilen. Dabei gibt es zwei Varianten:

Annotation

Die @ExtendWith Annotation aktiviert Mockito.

@ExtendWith(MockitoExtension.class)

Mocks Injecten

Mithilfe der @BeforeEach Annotation wird Mockito vor jedem Test mitgeteilt, dass es verwendet werden soll.

@BeforeEach
public void init() {
    MockitoAnnotations.openMocks(this);
}

Verwendung

Mock

Ist Mockito aktiviert, kann man davon profitieren.

Die am meisten verwendete Annotation ist @Mock. Sie ermöglicht es, eine Klasse zu erstellen, welche alle Funktionen bietet. Diese können aber trotzdem
manipuliert werden.

MyService mock = Mockito.mock(MyService.class);
Mockito.verify(mock).foo(bar);

Im folgenden Beispiel ist erkennbar, dass die Liste gemockt wurde. Nun können dieser mockedList Elemente hinzugefĂŒgt werden.
SpÀter ist es möglich, diese Liste zu manipulieren.

@ExtendWith(MockitoExtension.class)
class TestClass {
    @Mock
    List<String> mockedList;
 
    @Test
    public void checkSize() {
        mockedList.add("one");
    }
}

Auch bei den weiteren Beispielen wird diese Annotation verwendet.

Dummy

Ein Objekt ohne Implementierung wird als Dummy bezeichnet. Bekommt die zu testende Klasse eine Liste von Objekten, können diese Objekte durch
Dummys ersetzt werden. So ist die Klasse nicht abhÀngig von der Funktionsweise der Objekte.

MyObject dummy = Mockito.mock(MyObject.class);

Stub

Mithilfe eines Stubs kann der RĂŒckgabewert einer Methode manipuliert werden. Man kann also bestimmen, was eine Methode zurĂŒckgibt, ohne dessen
Implementierung zu kennen.

Im folgenden Beispiel wird getestet, wie gross die Liste ist. Auf Zeile 23 wird festgelegt, dass die Liste bei einer Anfrage nach der Grösse immer 100
zurĂŒckgibt. Somit ist schlussendlich nicht mehr wichtig, wie gross die Liste wirklich ist. Sie muss also nicht gefĂŒllt sein, damit der Test besteht.

@ExtendWith(MockitoExtension.class)
class TestClass {
    @Mock
    List<String> mockedList;
 
    @Test
    public void checkSize() {
        mockedList.add("one");
        Mockito.verify(mockedList).add("one");
        assertEquals(0, mockedList.size());
 
        // Stub
        Mockito.when(mockedList.size()).thenReturn(100);
        assertEquals(100, mockedList.size());
    }
}

Obwohl die Liste nicht nur 100 Strings enthÀlt, schlÀgt der Test nicht fehl.

Spy

Wenn die zu testende Klasse einen Service aufruft, um eine Aktion durchzufĂŒhren, kann ein Spy den Service ersetzen. Dieser ermöglicht es, die Objekte
zu beobachten.

List listSpy = Mockito.spy(new ArrayList());
listSpy.add(new Object());
Mockito.verify(listSpy).add(Matchers.any());
assertEquals(1, listSpy.size());

Ein Mock wird anhand der Klasse eines Typs erstellt, nicht anhand einer tatsÀchlichen Instanz.
Ein Spy umhĂŒllt eine bestehende Instanz. Der Unterschied liegt darin, dass man mit einem Spy alle Interaktionen verfolgen kann.

Fake

Eine vereinfachte Implementierung einer Klasse wird auch als Fake bezeichnet.
Die Klasse greift auf eine Datenbank zu. Dies tut sie ĂŒber einen Service. Dieser Service kann nun durch die Fake-Implementierung ersetzt werden.
Z. B. kann dieser Fake die Daten bei sich gespeichert haben und muss sie so nicht von der Datenbank holen.

ArgumentCaptor

Mit ArgumentCaptor kann man Parameter ĂŒberwachen. Wird eine Methode aufgerufen, ist feststellbar, welche Attribute dabei mitgegeben wurden.

ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);
verify(mock).add(arg.capture());
String s = arg.getValue();

In diesem Beispiel kann man aus dem String s den Parameter auslesen.

InjectMocks

@Mock erschafft ein Mock. Mit der @InjectMocks Annotation können Instanzen einer Klasse erstellt werden und Mocks oder Spies in die Klasse
injected werden.

@Mock
Map<String, String> wordMap;
 
@InjectMocks
MyDictionary dic = new MyDictionary();
 
@Test
public void TestClass() {
    Mockito.when(wordMap.get("aWord")).thenReturn("aMeaning");
    assertEquals("aMeaning", dic.getMeaning("aWord"));
}
 
public class MyDictionary {
    Map<String, String> wordMap;
 
    public MyDictionary() {
        wordMap = new HashMap<String, String>();
    }
 
    public void add(final String word, final String meaning) {
        wordMap.put(word, meaning);
    }
 
    public String getMeaning(final String word) {
        return wordMap.get(word);
    }
}

Fazit

Meiner Meinung nach ergeben die gekapselten Tests absolut Sinn. Ich verstehe nun auch, warum Mockito so beliebt ist und auch bei unseren Projekten eingesetzt wurde.