Skip to Content
2021Mockito

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.