Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lernen Mockito | Testen von Backend-Anwendungen
Spring Boot Backend
course content

Kursinhalt

Spring Boot Backend

Spring Boot Backend

1. Grundlagen der Backend-Entwicklung
2. Spring Boot Grundlagen
3. RESTful API
4. Arbeiten mit Datenbanken
5. Testen von Backend-Anwendungen

book
Mockito

Im vorherigen Kapitel haben wir uns daran erinnert, wie man Unit-Tests schreibt. In unserer Anwendung ist dies jedoch nicht immer praktisch, da das Testen eines Controllers das Testen des Service erfordert und das Testen des Service das Testen des Repository notwendig macht.

Diese Kette von Abhängigkeiten wirft die Frage auf: Müssen wir alle Module und ihre Abhängigkeiten testen, oder können wir ihr Verhalten simulieren — mit anderen Worten, sie mocken?

Mockito: Einführung

Mockito ist eine beliebte Bibliothek für Java, die verwendet wird, um Mock-Objekte in Unit-Tests zu erstellen.

Es ermöglicht Entwicklern, das Verhalten komplexer externer Abhängigkeiten (wie Datenbanken, Dienste oder APIs) zu simulieren, um die Logik einer bestimmten Komponente (normalerweise eine Klasse oder Methode) zu isolieren und zu testen, ohne mit diesen externen Systemen zu interagieren.

Dies macht Unit-Tests vorhersehbarer und schneller, da während des Testens keine echten externen Ressourcen aufgerufen werden müssen.

Mock-Objekte in Mockito

Dies ermöglicht Entwicklern, sicherzustellen, dass die zu testende Komponente (die Einheit) korrekt funktioniert, ohne sich auf echte Abhängigkeiten wie Datenbanken oder Dienste von Drittanbietern zu verlassen.

Eine Klasse kann mit @Mock annotiert werden, um ihre Mock-Version zu erstellen. Diese Annotation wird oft zusammen mit @InjectMocks verwendet, um Abhängigkeiten automatisch in die zu testende Klasse zu injizieren.

Dieses Mock-Objekt kann dann als Abhängigkeit in ein anderes Objekt mit der @InjectMocks Annotation injiziert werden. Diese Annotation injiziert automatisch die Mock-Objekte in die zu testende Klasse.

Schlüsselmethoden in Mockito

Mockito bietet eine breite Palette von Methoden zur Verwaltung von Mock-Objekten, wobei jede einem bestimmten Zweck im Testprozess dient. Im Folgenden sind die wichtigsten Methoden zusammen mit ihren Erklärungen aufgeführt.

Testen von Controllern mit Mockito

Das Testen von Controllern beinhaltet oft die Verwendung von Mocks für Services, die innerhalb des Controllers aufgerufen werden. In diesem Kontext spielt Mockito eine Schlüsselrolle, um die Logik des Controllers von der Service-Schicht zu isolieren.

Kurze Zusammenfassung des Videos

Wir testen Controller mit der Klasse MockMvc, aber was bietet sie und welche Vorteile hat die Verwendung?

Mit MockMvc können Sie verschiedene HTTP-Anfragen (GET, POST, PUT, etc.) simulieren, Parameter und Header übergeben und Antworten, Statuscodes, Header und Antwortinhalte überprüfen, was das Unit-Testing von Controllern erheblich erleichtert.

Zum Beispiel können Sie Methoden wie perform() verwenden, um die Anfrage auszuführen, andExpect(), um die erwarteten Ergebnisse zu überprüfen, und content(), um den Inhalt der Antwort zu prüfen.

java

Test

copy
1234
mockMvc.perform(put("/books/{id}", bookId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookRequestDTO))) .andExpect(status().isOk())

Zusätzlich können Sie Assertions verketten, um den HTTP-Status mit status().isOk() zu überprüfen, JSON-Antwortstrukturen mit jsonPath() zu verifizieren und mehr.

java

Test

copy
1234
.andExpect(jsonPath("$.id").value(bookResponseDTO.getId())) .andExpect(jsonPath("$.name").value(bookResponseDTO.getName())) .andExpect(jsonPath("$.author").value(bookResponseDTO.getAuthor())) .andExpect(jsonPath("$.price").value(bookResponseDTO.getPrice()));

Diese Werkzeuge ermöglichen eine umfassende Prüfung des Verhaltens des Controllers, ohne tatsächlich echte HTTP-Anfragen zu stellen.

Wichtige Anmerkungen

Die @WebMvcTest(BookController.class) Annotation wird verwendet, um den BookController zu testen, wobei nur die Komponenten geladen werden, die für diesen spezifischen Controller notwendig sind. Sie schließt den Rest der Spring-Infrastruktur aus, sodass Sie sich auf das Testen des Controllers selbst konzentrieren können.

Zusätzlich erstellt die @MockBean Annotation, die auf das bookService Feld angewendet wird, eine Mock-Version des Dienstes, die es Ihnen ermöglicht, sein Verhalten zu simulieren. Dies hilft, den Controller von seinen Abhängigkeiten zu isolieren und sich auf das Testen seiner Logik zu konzentrieren.

Tests für die updateBook-Methode

Wir haben zwei Tests für die updateBook-Methode geschrieben, die alle möglichen Fälle für diese Methode abdecken. Im ersten Test überprüfen wir, dass alles erfolgreich war und dass unsere Entität aktualisiert wurde.

java

BookControllerTest

copy
1234567891011121314151617
@Test void testUpdateBook_whenBookExists_shouldReturnUpdatedBook() throws Exception { String bookId = "1"; when(bookService.updateBook(bookId, bookRequestDTO)).thenReturn(bookResponseDTO); mockMvc.perform(put("/books/{id}", bookId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookRequestDTO))) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value(bookResponseDTO.getId())) .andExpect(jsonPath("$.name").value(bookResponseDTO.getName())) .andExpect(jsonPath("$.author").value(bookResponseDTO.getAuthor())) .andExpect(jsonPath("$.price").value(bookResponseDTO.getPrice())); verify(bookService).updateBook(bookId, bookRequestDTO); }

Dieser Test überprüft die erfolgreiche Aktualisierung eines book, wenn es in der Datenbank existiert. Die updateBook()-Methode des Service wird mit der Id des Buches und einem bookRequestDTO-Objekt aufgerufen, wonach sie ein bookResponseDTO-Objekt zurückgibt, das das aktualisierte Buch darstellt.

Überprüfung der Ausnahmebehandlung

Wir haben auch einen Test, bei dem eine Ausnahme in der updateBook-Methode auftritt, und wir müssen überprüfen, wie sich der Controller in dieser Situation verhält.

java

BookControllerTest

copy
12345678910111213141516
@Test void testUpdateBook_whenBookNotFound_shouldReturnApiException() throws Exception { String bookId = "1"; String errorMessage = "ID not found"; when(bookService.updateBook(bookId, bookRequestDTO)) .thenThrow(new ApiException(errorMessage, HttpStatus.NOT_FOUND)); mockMvc.perform(put("/books/{id}", bookId) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(bookRequestDTO))) .andExpect(status().isNotFound()) .andExpect(jsonPath("$.error").value(errorMessage)); verify(bookService).updateBook(bookId, bookRequestDTO); }

Dieser Test überprüft das Verhalten des Controllers, wenn versucht wird, ein Buch zu aktualisieren, das in der Datenbank nicht gefunden wird. Die updateBook()-Methode des Service wird zuerst mit der Id des Buches und einem bookRequestDTO-Objekt aufgerufen.

Anstatt jedoch eines erfolgreichen Ergebnisses wirft der Service eine ApiException, die anzeigt, dass das Buch mit der angegebenen Id nicht gefunden wurde.

Servicetests mit Mockito

Beim Testen eines Service ist es wichtig, ihn von Abhängigkeiten wie Repositories oder externen APIs zu isolieren. Mockito ermöglicht es Ihnen, Mocks für diese Abhängigkeiten zu erstellen und deren Verhalten in den Tests zu definieren.

Kurze Zusammenfassung des Videos

Beginnen wir mit dem Testen unserer Methode, wenn sie die Daten erfolgreich aktualisiert, was bedeutet, dass die Id gültig ist. Natürlich verwenden wir Mockito, um das Verhalten des Repositories zu mocken.

java

BookServiceTest

copy
123456789101112131415161718192021222324252627282930313233
@Test void testUpdateBook_whenBookExists_shouldUpdateAndReturnBook() { BookRequestDTO bookRequestDTO = new BookRequestDTO(); bookRequestDTO.setName("Updated Name"); bookRequestDTO.setAuthor("Updated Author"); bookRequestDTO.setPrice("150"); Book bookWithRepository = new Book(); bookWithRepository.setId("1"); bookWithRepository.setName("Original Name"); bookWithRepository.setAuthor("Original Author"); bookWithRepository.setPrice("100"); Book updateBook = new Book(); updateBook.setId("1"); updateBook.setName("Updated Name"); updateBook.setAuthor("Updated Author"); updateBook.setPrice("150"); when(bookRepository.findById("1")).thenReturn(Optional.of(bookWithRepository)); when(bookRepository.save(bookWithRepository)).thenReturn(updateBook); BookResponseDTO result = bookService.updateBook("1", bookRequestDTO); assertNotNull(result); assertEquals("1", result.getId()); assertEquals("Updated Name", result.getName()); assertEquals("Updated Author", result.getAuthor()); assertEquals("150", result.getPrice()); verify(bookRepository).findById("1"); verify(bookRepository).save(bookWithRepository); }

Die Methode bookRepository.findById("1") wird gemockt, um ein vorhandenes Book aus dem Repository zurückzugeben, und bookRepository.save(bookWithRepository) wird gemockt, um das aktualisierte Buch nach dem Speichern der Änderungen zurückzugeben.

Die updateBook() Servicemethode wird aufgerufen, um das Buch basierend auf dem bookRequestDTO zu aktualisieren und gibt ein BookResponseDTO zurück.

assertNotNull(result) stellt sicher, dass das Ergebnis nicht null ist, was auf eine erfolgreiche Aktualisierung hinweist, während assertEquals() überprüft, dass die ID, der name, der author und der price des aktualisierten Buches den erwarteten Werten entsprechen.

Schließlich stellt verify() sicher, dass findById() und save() im Repository mit den korrekten Parametern aufgerufen wurden.

Überprüfung einer Ausnahme im Service

Dieser Test überprüft, dass der Service eine ApiException auslöst, wenn das zu aktualisierende Book nicht in der Datenbank gefunden wird.

java

BookServiceTest

copy
1234567891011121314151617181920
@Test void testUpdateBook_whenBookNotFound_shouldThrowApiException() { BookRequestDTO bookRequestDTO = new BookRequestDTO(); bookRequestDTO.setName("Updated Name"); bookRequestDTO.setAuthor("Updated Author"); bookRequestDTO.setPrice("150"); String idTest = "999"; when(bookRepository.findById(idTest)).thenReturn(Optional.empty()); ApiException apiException = assertThrows(ApiException.class, () -> { bookService.updateBook(idTest, bookRequestDTO); }); assertEquals("Not found book by id: " + idTest, apiException.getMessage()); assertEquals(HttpStatus.NOT_FOUND, apiException.getHttpStatus()); verify(bookRepository, never()).save(any(Book.class)); }

Zunächst wird ein BookRequestDTO Objekt mit den Daten für die Aktualisierung erstellt, und eine Testbuch-Id, die nicht in der Datenbank existiert, "999", wird zugewiesen. Die Methode bookRepository.findById(idTest) wird gemockt, um Optional.empty() zurückzugeben, was anzeigt, dass es kein Buch mit dieser Id gibt.

Der Test verwendet die assertThrows() Methode, um zu überprüfen, dass beim Aufruf von bookService.updateBook(idTest, bookRequestDTO) eine ApiException ausgelöst wird.

Anschließend überprüfen die assertEquals() Methoden, dass die Ausnahme-Nachricht dem erwarteten Text entspricht und dass der Fehlerstatus gleich HttpStatus.NOT_FOUND ist.

Die Methode verify(bookRepository, never()).save(any(Book.class)) stellt sicher, dass die Buchspeicher-Methode save() nicht aufgerufen wurde, da das Buch nicht gefunden wurde und die Aktualisierung nicht durchgeführt wurde.

Zusammenfassung

Mockito ist eine Bibliothek zur Erstellung von Mock-Objekten, die es Ihnen ermöglicht, das Verhalten von Abhängigkeiten zu simulieren und den Code unter Test zu isolieren. Dies vereinfacht das Testen, macht es schneller, stabiler und unabhängig von externen Ressourcen.

War alles klar?

Wie können wir es verbessern?

Danke für Ihr Feedback!

Abschnitt 5. Kapitel 3
We're sorry to hear that something went wrong. What happened?
some-alt