Mockito
Mockito ist ein Framework für das Erstellen von Mock-Objekten in Java. Mock-Objekte simulieren das Verhalten realer Komponenten und ersetzen diese in Tests. Dadurch werden Abhängigkeiten des SUT (System under Test) reduziert und die Tests isoliert (Test-Isolierung). Typische Gründe für Mock-Objekte sind komplexe Abhängigkeiten, unerwünschte Seiteneffekte oder hohe Laufzeiten realer Komponenten.
Arten von Mock-Objekten
Mockito unterscheidet zwischen drei Arten von Testobjekten:
- Ein Stub gibt unabhängig von der Eingabe immer denselben festgelegten Wert zurück.
- Ein Mock gibt abhängig von der Eingabe unterschiedliche festgelegte Werte zurück.
- Ein Spy protokolliert Methodenaufrufe und die übergebenen Argumente, sodass sie im Test abgefragt werden können.
Simulieren von Objekten
Mock-Objekte werden entweder mit der statischen Methode T mock(reified: T...)
der Klasse Mockito oder mit der Annotation @Mock in Verbindung mit
MockitoAnnotations.openMocks() erstellt. Ein so erzeugtes Objekt hat keine
Methodenimplementierungen. Das gewünschte Verhalten wird über
Mockito.when(...).thenReturn(...) bzw. .thenThrow(...) festgelegt.
Umhüllen von Objekten
Spy-Objekte werden mit T spy(object: T) der Klasse Mockito oder mit der
Annotation @Spy erstellt. Im Gegensatz zum Mock delegiert ein Spy
standardmäßig an die echte Implementierung. Einzelne Methoden können mit
Mockito.doReturn(...).when(...) überschrieben werden.
Prüfen von Methodenaufrufen
Mit Mockito.verify(mock, mode) wird geprüft, ob und wie oft eine Methode
aufgerufen wurde. Als Modus stehen u.a. times(n), atLeast(n), atMost(n)
und never() zur Verfügung.
Argument Matcher
Die Klasse ArgumentMatchers bietet statische Methoden wie anyString() oder
anyList(), um beim Simulieren und Verifizieren flexible Eingabebedingungen
festzulegen.
Beispiel
- Zu testende Klasse
- Zu simulierende Klasse
- Testklasse
@Data
public class StudentClass {
private List<Student> students;
public void addStudent(Student student) {
students.add(student);
}
public Optional<Student> getStudentWithBestAverageGrade() {
Student studentWithBestAverageGrade = null;
double bestAverageGrade = Double.MAX_VALUE;
for (Student s : students) {
if (s.getAverageGrade() < bestAverageGrade) {
studentWithBestAverageGrade = s;
bestAverageGrade = s.getAverageGrade();
}
}
return Optional.ofNullable(studentWithBestAverageGrade);
}
}
@Data
public class Student {
private final int id;
private final String name;
private final List<Double> grades;
public void addGrade(double grade) {
grades.add(grade);
}
public double getAverageGrade() {
double total = 0;
for (double grade : grades) {
total += grade;
}
return total / grades.size();
}
}
public class StudentClassTest {
@Mock
private Student studentMock;
private Student studentSpy;
private StudentClass studentClass;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
studentSpy = spy(new Student(1, "Hans Maier", new ArrayList<>()));
studentClass = new StudentClass(new ArrayList<>());
studentClass.addStudent(studentMock);
studentClass.addStudent(studentSpy);
}
@Test
void testGetStudentWithBestAverageGrade() {
when(studentMock.getAverageGrade()).thenReturn(2.2);
doReturn(1.5).when(studentSpy).getAverageGrade();
assertEquals(Optional.of(studentSpy), studentClass.getStudentWithBestAverageGrade());
verify(studentMock, atLeast(1)).getAverageGrade();
verify(studentSpy, atLeast(1)).getAverageGrade();
}
}