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.