Unterschied zwischen der Verwendung von MockMvc mit SpringBootTest und der Verwendung von WebMvcTest

95

Ich bin neu in Spring Boot und versuche zu verstehen, wie das Testen in SpringBoot funktioniert. Ich bin etwas verwirrt darüber, was der Unterschied zwischen den folgenden beiden Codefragmenten ist:

Code-Snippet 1:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerApplicationTest {
    @Autowired    
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

Dieser Test verwendet die @WebMvcTestAnnotation, die meiner Meinung nach für Feature-Slice-Tests gedacht ist, und testet nur die MVC-Schicht einer Webanwendung.

Code-Snippet 2:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

Dieser Test verwendet die @SpringBootTestAnmerkung und a MockMvc. Wie unterscheidet sich das von Code-Snippet 1? Was macht das anders?

Bearbeiten: Hinzufügen von Code-Snippet 3 (Dies wurde als Beispiel für Integrationstests in der Spring-Dokumentation gefunden.)

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
public class HelloControllerIT {
    
    @LocalServerPort private int port;
    private URL base;
    
    @Autowired private TestRestTemplate template;
    
    @Before public void setUp() throws Exception {
        this.base = new URL("http://localhost:" + port + "/");
    }
    
    @Test public void getHello() throws Exception {
        ResponseEntity < String > response = template.getForEntity(base.toString(), String.class);
        assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
    }
}
Revansha
quelle

Antworten:

88

@SpringBootTestist die allgemeine Testanmerkung. Wenn Sie nach etwas suchen, das vor 1.4 dasselbe tut, sollten Sie dieses verwenden. Es wird überhaupt kein Slicing verwendet, was bedeutet, dass der gesamte Anwendungskontext gestartet und das Scannen von Komponenten überhaupt nicht angepasst wird.

@WebMvcTestwird nur den von Ihnen definierten Controller und die MVC-Infrastruktur scannen. Das ist es. Wenn Ihr Controller also von anderen Beans Ihrer Service-Schicht abhängig ist, wird der Test erst gestartet, wenn Sie diese Konfiguration entweder selbst laden oder einen Mock dafür bereitstellen. Dies ist viel schneller, da wir nur einen winzigen Teil Ihrer App laden. Diese Anmerkung verwendet das Schneiden.

Das Lesen des Dokuments sollte Ihnen wahrscheinlich auch helfen.

Stephane Nicoll
quelle
Vielen Dank für die Antwort !!. Wenn ich Sie also richtig verstehe, bedeutet dies, dass beide Codefragmente nur den MVC-Teil der Anwendung testen. Tcode-Snippet 1 lädt jedoch den vollständigen Anwendungskontext, während Code-Snippet 2 nur den Controller scannt. Ist das richtig? Kann das Code-Snippet 1 als Komponententest zum Testen des Controllers betrachtet werden?
Revansha
1
Nein, es ist nicht richtig. SpringBootTestlädt Ihre vollständige App (bis zu einem gewissen Grad wird der eingebettete Container standardmäßig nicht gestartet, wenn einer verfügbar ist, dafür webEnvironmentist der da). Ich würde nicht sagen, dass dies @SpringBootTestein Komponententest des Controllers ist, sondern eher ein Integrationstest. WebMvcTestist wirklich ein Komponententest Ihres Controllers in dem Sinne, dass Sie ihn selbst bereitstellen müssen, wenn er abhängig ist (entweder eine Konfiguration oder eine Art Mock).
Stephane Nicoll
Nochmals vielen Dank für die Antwort. Ich habe die Frage bearbeitet und das Code-Snippet 3 hinzugefügt. Sie haben erwähnt, dass die Annotation @SpringBootTest eher für Integrationstests verwendet wird. Ich glaube, Snippet 3 demonstriert dies. Wenn also Integrationstests wie in Snippet 3 durchgeführt werden, was macht Snippet 2 dann? Snippet 2 verwendet die SpringBootTest-Annotation und eine Scheinumgebung (Standardwert des wenEnvironment-Attributs). Außerdem startet Snippet 3 den eingebetteten Server und führt wirklich HTTP-Aufrufe durch, während Snippet 2 dies nicht tut. Kann man Snippet 2 nicht als Unit-Test betrachten?
Revansha
4
Ich bin nicht sicher, ob wir das hier klären werden. Vielleicht Gitter? Die Sache , dass Sie ständig zu vermissen scheinen , ist , dass der Anwendungskontext , dass SpringBootTestund WebMvcTestschaffen sind sehr unterschiedlich. Ersteres lädt Ihre GANZE App und aktiviert ALLE automatischen Konfigurationen, während letzteres nur Spring Mvc aktiviert und nur scannt HelloController. Es kommt doch darauf an, was Sie unter einem Unit-Test verstehen. Aber das ist der Unterschied.
Stephane Nicoll
Vielen Dank für Ihre Antwort. Das ist sehr hilfreich für mich. Jetzt verstehe ich, warum mein Test mit SpringBootTest ausgeführt werden kann, aber mit Ausnahme von WebMvcTest. Nochmals vielen Dank.
Alps1992
69

Die Annotation @SpringBootTest weist Spring Boot an, nach einer Hauptkonfigurationsklasse zu suchen (z. B. mit @SpringBootApplication) und damit einen Spring-Anwendungskontext zu starten. SpringBootTest lädt die vollständige Anwendung und injiziert alle Bohnen, die langsam sein können.

@WebMvcTest - Zum Testen der Controller-Schicht müssen Sie die verbleibenden Abhängigkeiten angeben, die mit Mock Objects erforderlich sind.

Einige weitere Anmerkungen als Referenz.

Testen von Slices der Anwendung Manchmal möchten Sie ein einfaches „Slice“ der Anwendung testen, anstatt die gesamte Anwendung automatisch zu konfigurieren. Spring Boot 1.4 führt 4 neue Testanmerkungen ein:

@WebMvcTest - for testing the controller layer
@JsonTest - for testing the JSON marshalling and unmarshalling
@DataJpaTest - for testing the repository layer
@RestClientTests - for testing REST clients

Weitere Informationen finden Sie unter: https://spring.io/guides/gs/testing-web/

RoshanKumar Mutha
quelle
Hier ist ein Link zu Sping Boot Reference - Anmerkungen zur automatischen Konfiguration testen . Es gibt mehr als nur die vier @ roshankumar-mutha, die hier aufgelistet sind. Der Link zur Anleitung für die ersten Schritte behandelt diese Abschnitte nicht ausführlich.
George Pantazes
15

MVC-Tests sollen nur den Controller-Teil Ihrer Anwendung abdecken. HTTP-Anforderungen und -Antworten werden verspottet, sodass keine echten Verbindungen hergestellt werden. Wenn Sie dagegen verwenden @SpringBootTest, wird die gesamte Konfiguration für den Webanwendungskontext geladen und die Verbindungen werden über den realen Webserver ausgeführt. In diesem Fall verwenden Sie nicht die MockMvcBean, sondern einen Standard RestTemplate(oder die neue Alternative TestRestTemplate).

Also, wann sollten wir den einen oder anderen wählen? @WebMvcTestsoll den Controller von der Serverseite aus einheitlich testen. @SpringBootTestAndererseits sollte es für Integrationstests verwendet werden, wenn Sie von der Clientseite aus mit der Anwendung interagieren möchten.

Das bedeutet nicht, dass Sie keine Mocks verwenden können @SpringBootTest. Wenn Sie einen Integrationstest schreiben, kann dies dennoch erforderlich sein. In jedem Fall ist es besser, es nicht nur für den Unit-Test eines einfachen Controllers zu verwenden.

Quelle - Lernen von Microservices mit Spring Boot

Gautam Tadigoppula
quelle
1
Ich verstehe nicht, warum diese Antwort positiv bewertet wird. Wenn Sie verwenden @SpringBootTest, wird ein echter Webserver erst gestartet, wenn Sie auch webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT(oder a DEFINED_PORT) haben und Verbindungen nicht über den echten Webserver hergestellt werden. Der Standardwert für @SpringBootTestist WebEnvironment.MOCK.
Koray Tugay