Neuigkeiten von trion.
Immer gut informiert.

Spring MockMvc Tests mit AssertJ

Spring Boot

Spring Framework 6.2 bzw. Spring Boot 3.4 bringt eine interessante Neuerung für Entwickler, die ihre Unit- und Integrationstests mit AssertJ schreiben: MockMvc Tests können nun auch mit AssertJ formuliert werden (GitHub Issue). Bisher musste für Controller-Tests mit MockMvc auf die Hamcrest-Syntax zurückgegriffen werden, was zu uneinheitlichen Test-Code führen konnte. Mit dem Release von Spring Boot 3.4 im November 2024 kann die vertraute AssertJ assertThat() API auch in Verbindung mit MockMvc verwendet werden, was das Testen von Web-Controllern eleganter und konsistenter macht. In diesem Artikel zeigen wir, welche Anpassungen am Code notwendig sind, um diese neue Möglichkeit nutzen zu können.

Der Code in diesem Post wurde mit Spring Boot 3.4.0-M3 getestet.

In einer Spring Boot Anwendung gibt es grundsätzlich drei Möglichkeiten, Assertions für JUnit Tests zu formulieren: mit JUnit Bordmitteln oder mittels der Libraries Hamcrest oder AssertJ.

assertEquals(a, b); // JUnit
assertThat(a, equalTo(b)); //Hamcrest
assertThat(a).isEqualTo(b); //AssertJ

Mein persönlicher Favorit ist dabei AssertJ. Durch das Verketten von Methodenaufrufen nach assertThat entsteht flüssig lesbarer Code. Außerdem wird das Schreiben von Assertions durch die typabhängige Autocompletion der IDE enorm erleichtert.

Beim Testen von Spring Controllern musste man seine Assertions bisher jedoch in einem anderen Stil schreiben. Hier ein Beispiel für einen Test, der prüft, dass ein Actuator Endpunkt mit Spring Security abgesichert ist.

@SpringBootTest
@AutoConfigureMockMvc
class ActuatorTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void actuator_health_requires_auth() {
        ResultActions resultActions = mockMvc
                .perform(get("/actuator/health"));

        resultActions.andExpect(status().isUnauthorized());
    }
}

Einstiegspunkt ist die Klasse MockMvc, mit der wir eine Anfrage an einen Endpunkt simulieren können. Als Ergebnis wird eine Instanz der Klasse ResultActions zurückgegeben. Darüber können nun mit andExpect() Erwartungen formuliert werden. Zugriff auf Eigenschaften der HTTP Response wird über statische Methoden aus MockMvcResultMatchers bereitgestellt.

Mit Spring Boot 3.4 kann dieser Code mit überschaubarem Aufwand so umgeschrieben werden, dass das vertraute assertThat() von AssertJ verwendet werden kann. Im Wesentlichen sind dafür folgende Anpassungen vorzunehmen:

  • Aus MockMvc wird die neue Klasse MockMvcTester

  • Aus ResultActions wird MvcTestResult

  • resultActions.andExpect(…​) wird zu assertThat(testResult)

Die größten Änderungen entstehen bei den Assertions selbst. Hier einige Beispiele für die ResultActions Syntax:

resultActions.andExpect(status().isOk());
resultActions.andExpect(status().isUnauthorized());
resultActions.andExpect(status().is5xxServerError());
resultActions.andExpect(content().contentTypeCompatibleWith(
        MediaType.APPLICATION_JSON
    );
resultActions.andExpect(jsonPath("$", contains(...)));

Und hier die Entsprechungen mit MvcTestResult und AssertJ:

assertThat(testResult).hasStatusOk();
assertThat(testResult).hasStatus(HttpStatus.UNAUTHORIZED);
assertThat(testResult).hasStatus5xxServerError();
assertThat(testResult).hasContentTypeCompatibleWith(
        MediaType.APPLICATION_JSON
    );
assertThat(testResult).bodyJson().isLenientlyEqualTo("expected.json");
assertThat(testResult)
            .bodyJson()
            .extractingPath("$")
            ...

Unseren Test vom Actuator Endpunkt können wir also wie folgt umschreiben:

@SpringBootTest
@AutoConfigureMockMvc
class ActuatorTest {

    @Autowired
    private MockMvcTester mockMvcTester;

    @Test
    void actuator_health_requires_auth() {
        MvcTestResult testResult = mockMvcTester
                .perform(get("/actuator/health"));

        assertThat(testResult).hasStatus(HttpStatus.UNAUTHORIZED);
    }
}

Da der MockMvcTester intern auf MockMvc aufbaut, können wir uns diesen in Verbindung mit @AutoConfigureMockMvc einfach autowiren lassen.

Wie bei MockMvc wird auch beim MockMvcTester ein Standalone Setup ermöglicht.

class ActuatorTest {

    private final MockMvcTester mockMvc
        = MockMvcTester.of(new MyController());

    // ...

}

Durch ein Standalone Setup kommt man ohne Spring Context aus, wodurch die Testausführung beschleunigt wird und man näher an einem Unit-Test ist.




Über viele weitere Neuerungen seit Spring Boot 3 informieren wir in einer eigenen Schulung:

Zu den Themen Spring und Spring Boot bieten wir zudem sowohl Beratung, Entwicklungsunterstützung als auch passende Schulungen an:

Auch für Ihren individuellen Bedarf können wir Workshops und Schulungen anbieten. Sprechen Sie uns gerne an.

Feedback oder Fragen zu einem Artikel - per Twitter @triondevelop oder E-Mail freuen wir uns auf eine Kontaktaufnahme!

Los geht's!

Bitte teilen Sie uns mit, wie wir Sie am besten erreichen können.