Java >> Java Tutorial >  >> Tag >> Spring

Unit Tests für eine Spring MVC REST API schreiben:Konfiguration

Zuvor haben wir gelernt, dass wir das zu testende System mithilfe der Standalone-Konfiguration konfigurieren sollten, wenn wir Komponententests für Spring MVC-Controller schreiben.

In diesem Blogbeitrag setzen wir die Theorie in die Praxis um. Dieser Blogbeitrag beschreibt, wie wir die Standalone-Konfiguration verwenden können, wenn wir Komponententests für Spring MVC-Controller schreiben, die eine REST-API implementieren.

Nachdem wir diesen Blogbeitrag fertiggestellt haben, werden wir:

  • Verstehen Sie, wie wir die erforderlichen Komponenten erstellen und konfigurieren können, ohne unserer Testsuite doppelten Code hinzuzufügen.
  • Wissen, wie wir HTTP-Anfragen an das zu testende System senden können, ohne unserer Testsuite doppelten Code hinzuzufügen.
  • Kann das Spring MVC Test Framework konfigurieren, wenn wir Komponententests für eine Spring MVC REST API mit JUnit 5 schreiben.

Fangen wir an.

Einführung in das zu testende System

Das zu testende System besteht aus zwei Klassen:

  • Der TodoItemCrudController -Klasse enthält die Controller-Methoden, die eine REST-API implementieren, die CRUD-Vorgänge für ToDo-Elemente bereitstellt.
  • Die TodoItemCrudService -Klasse bietet CRUD-Vorgänge für ToDo-Elemente. Die TodoItemCrudController -Klasse ruft ihre Methoden auf, wenn sie HTTP-Anforderungen verarbeitet.

Der relevante Teil des TodoItemCrudController Klasse sieht wie folgt aus:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/todo-item")
public class TodoItemCrudController {

    private final TodoItemCrudService service;

    @Autowired
    public TodoItemCrudController(TodoItemCrudService service) {
        this.service = service;
    }
}

Als Nächstes erstellen wir die Komponenten, die die minimale Spring MVC-Konfiguration erweitern, die erstellt wird, wenn wir das zu testende System mithilfe der eigenständigen Konfiguration konfigurieren.

Erforderliche Komponenten erstellen

Wie wir uns erinnern, sollten wir die Anzahl der benutzerdefinierten Komponenten minimieren, die wir in das zu testende System einbauen. Es kann jedoch schwierig sein, die wesentlichen Komponenten zu identifizieren, wenn wir nicht viel Erfahrung haben. Deshalb habe ich zwei Regeln geschrieben, die uns bei der Auswahl der benötigten Komponenten helfen:

  • Wir sollten einen benutzerdefinierten HttpMessageConverter erstellen und konfigurieren wenn wir einen benutzerdefinierten ObjectMapper verwenden möchten die JSON in Objekte umwandelt und umgekehrt.
  • Wenn ein Locale Objekt als Methodenparameter in eine Methode des getesteten Controllers eingefügt wird, müssen wir einen benutzerdefinierten LocaleResolver erstellen und konfigurieren .

Wir können die erforderlichen Komponenten erstellen und konfigurieren, indem wir diesen Schritten folgen:

Zuerst , müssen wir einen public erstellen Objektmutterklasse, die die Factory-Methoden enthält, die die erforderlichen Komponenten erstellen und konfigurieren. Nachdem wir unsere Objekt-Mutterklasse erstellt haben, müssen wir sicherstellen, dass niemand sie instanziieren kann.

Nachdem wir unsere Objekt-Mutterklasse erstellt haben, sieht ihr Quellcode wie folgt aus:

public final class WebTestConfig {
 
    private WebTestConfig() {}
}

Zweiter , müssen wir einen public hinzufügen und static Methode namens objectMapper() zu unserer Objektmutterklasse. Diese Methode erstellt und konfiguriert einen neuen ObjectMapper Objekt und gibt das erstellte Objekt zurück. Unsere Tests verwenden diese Methode, wenn sie JSON-Dokumente erstellen, die an das zu testende System gesendet werden.

Nachdem wir diese Methode geschrieben haben, sieht der Quellcode unserer Objekt-Mutterklasse wie folgt aus:

import com.fasterxml.jackson.databind.ObjectMapper;

public final class WebTestConfig {
    
    private WebTestConfig() {}
    
    public static ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

Dritter , müssen wir einen public hinzufügen und static Methode namens objectMapperHttpMessageConverter() zu unserer Objektmutterklasse. Unsere Testklassen verwenden diese Methode, wenn sie das zu testende System konfigurieren. Nachdem wir diese Methode zu unserer Objekt-Mutterklasse hinzugefügt haben, müssen wir sie wie folgt implementieren:

  1. Neuen MappingJackson2HttpMessageConverter erstellen Objekt. Dieses Objekt kann mithilfe von Jackson JSON lesen und schreiben.
  2. Konfigurieren Sie den ObjectMapper das wird vom erstellten MappingJackson2HttpMessageConverter verwendet Objekt.
  3. Gib das erstellte Objekt zurück.

Nachdem wir diese Methode implementiert haben, sieht der Quellcode unserer Objekt-Mutterklasse wie folgt aus:

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;

public final class WebTestConfig {

    private WebTestConfig() {}

    public static MappingJackson2HttpMessageConverter objectMapperHttpMessageConverter() {
        MappingJackson2HttpMessageConverter converter = 
                new MappingJackson2HttpMessageConverter();
        converter.setObjectMapper(objectMapper());
        return converter;
    }

    public static ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

Wir können jetzt die erforderlichen Komponenten erstellen, indem wir eine Objektmutterklasse verwenden. Lassen Sie uns weitermachen und herausfinden, wie wir eine Anforderungserstellungsklasse erstellen können, die HTTP-Anforderungen an das zu testende System sendet.

Erstellen der Request Builder-Klasse

Wenn wir Komponententests für eine reale Webanwendung oder eine REST-API schreiben, stellen wir fest, dass jede Testmethode eine neue HTTP-Anforderung erstellt und an das zu testende System sendet. Dies ist eine schlechte Situation, da doppelter Code das Schreiben und Verwalten unserer Tests erschwert.

Wir können dieses Problem lösen, indem wir Request-Builder-Klassen verwenden. Eine Request-Builder-Klasse ist eine Klasse, die diese Bedingungen erfüllt:

  • Es enthält Methoden, die HTTP-Anforderungen erstellen und an das zu testende System senden, indem sie einen MockMvc verwenden Objekt.
  • Jede Methode muss einen ResultActions zurückgeben -Objekt, das es uns ermöglicht, Assertionen für die zurückgegebene HTTP-Antwort zu schreiben.

Wir können unsere Request-Builder-Klasse schreiben, indem wir diesen Schritten folgen:

  1. Neuen Kurs erstellen.
  2. Fügen Sie einen private MockMvc hinzu Feld zur erstellten Klasse. Unsere Anforderungserstellungsklasse verwendet dieses Feld, wenn sie HTTP-Anforderungen erstellt und an das zu testende System sendet.
  3. Stellen Sie sicher, dass wir den verwendeten MockMvc injizieren können Objekt in mockMvc -Feld mithilfe der Konstruktorinjektion.

Nachdem wir unsere Request-Builder-Klasse erstellt haben, sieht ihr Quellcode wie folgt aus:

import org.springframework.test.web.servlet.MockMvc;

class TodoItemRequestBuilder {

    private final MockMvc mockMvc;

    TodoItemRequestBuilder(MockMvc mockMvc) {
        this.mockMvc = mockMvc;
    }
}

Als nächstes werden wir lernen, das zu testende System zu konfigurieren.

Konfigurieren des zu testenden Systems

Wir können eine neue Testklasse erstellen und das zu testende System konfigurieren, indem wir diesen Schritten folgen:

Zuerst , müssen wir eine neue Testklasse erstellen und die erforderlichen Felder zu unserer Testklasse hinzufügen. Unsere Testklasse hat zwei private Felder:

  1. Der requestBuilder Feld enthält den TodoItemRequestBuilder Objekt, das von unseren Testmethoden verwendet wird, wenn sie HTTP-Anforderungen an das zu testende System senden.
  2. Die service Feld enthält einen TodoItemCrudService spotten. Unsere Setup-Methoden verwenden dieses Feld, wenn sie Methoden mit Mockito abbrechen. Außerdem verwenden unsere Testmethoden dieses Feld, wenn sie die Interaktionen überprüfen, die zwischen dem zu testenden System und unserem Mock stattgefunden haben oder nicht stattgefunden haben.

Nachdem wir unsere Testklasse erstellt haben, sieht ihr Quellcode wie folgt aus:

class TodoItemCrudControllerTest {

    private TodoItemRequestBuilder requestBuilder;
    private TodoItemCrudService service;
}

Zweiter , haben wir eine neue Setup-Methode geschrieben, die ausgeführt wird, bevor eine Testmethode ausgeführt wird, und implementieren diese Methode, indem Sie diesen Schritten folgen:

  1. Erstellen Sie einen neuen TodoItemCrudService mock und speichere das erstellte Mock im service Feld.
  2. Erstellen Sie einen neuen TodoItemCrudController Objekt (das ist der getestete Controller) und das erstellte Objekt in einer lokalen Variablen speichern.
  3. Erstellen Sie einen neuen MockMvc Objekt, indem Sie die Standalone-Konfiguration verwenden und das erstellte Objekt in einer lokalen Variablen speichern. Denken Sie daran, eine benutzerdefinierte Fehlerbehandlungsklasse (auch bekannt als @ControllerAdvice) zu konfigurieren Klasse) und einen benutzerdefinierten HttpMessageConverter die JSON lesen und schreiben kann, indem sie Jackson (auch bekannt als MappingJackson2HttpMessageConverter ).
  4. Erstellen Sie einen neuen TodoItemRequestBuilder Objekt und speichern Sie das erstellte Objekt im requestBuilder Feld.

Nachdem wir unsere Setup-Methode geschrieben haben, sieht der Quellcode unserer Testklasse wie folgt aus:

import org.junit.jupiter.api.BeforeEach;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static net.petrikainulainen.springmvctest.junit5.web.WebTestConfig.*;
import static org.mockito.Mockito.mock;

class TodoItemCrudControllerTest {

    private TodoItemRequestBuilder requestBuilder;
    private TodoItemCrudService service;

    @BeforeEach
    void configureSystemUnderTest() {
        service = mock(TodoItemCrudService.class);

        TodoItemCrudController testedController = new TodoItemCrudController(service);
        MockMvc mockMvc = MockMvcBuilders.standaloneSetup(testedController)
                .setControllerAdvice(new TodoItemErrorHandler())
                .setMessageConverters(objectMapperHttpMessageConverter())
                .build();
        requestBuilder = new TodoItemRequestBuilder(mockMvc);
    }
}

Wir können jetzt das zu testende System konfigurieren, indem wir die Standalone-Konfiguration verwenden. Fassen wir zusammen, was wir aus diesem Blogbeitrag gelernt haben.

Zusammenfassung

Dieser Blogpost hat uns Folgendes gelehrt:

  • Wir können die erforderlichen benutzerdefinierten Komponenten erstellen, ohne doppelten Code zu schreiben, indem wir einen public verwenden Objektmutterklasse mit public und static Fabrikmethoden.
  • Wir können HTTP-Anfragen an das zu testende System senden, ohne doppelten Code schreiben zu müssen, indem wir eine Request-Builder-Klasse verwenden.
  • Wenn wir das zu testende System mit der eigenständigen Konfiguration konfigurieren möchten, müssen wir den standaloneSetup() aufrufen Methode des MockMvcBuilders Klasse.
  • Wir können benutzerdefinierte Komponenten in das zu testende System einbinden, indem wir die Methoden des StandaloneMockMvcBuilder verwenden Klasse.
  • Die häufigsten benutzerdefinierten Komponenten, die im getesteten System enthalten sind, sind:ein benutzerdefinierter @ControllerAdvice Klasse und und ein benutzerdefiniertes HttpMessageConverter die JSON lesen und schreiben kann, indem sie Jackson (auch bekannt als MappingJackson2HttpMessageConverter ).

Java-Tag