Integrationstest mit Maven

Integrationstests mit Maven

1. Überblick

Maven ist das beliebteste Build-Tool im Java-Bereich, während Integrationstests ein wesentlicher Bestandteil des Entwicklungsprozesses sind. Daher istit’s a natural choice to configure and execute integration tests with Maven.

In diesem Tutorial werden verschiedene Möglichkeiten beschrieben, wie Sie Maven für Integrationstests verwenden und Integrationstests von Komponententests trennen können.

2. Vorbereitung

Um den Demonstrationscode einem realen Projekt nahe zu bringen, richten wir eine JAX-RS-Anwendung ein. Diese Anwendung wird vor der Ausführung von Integrationstests auf einem Server bereitgestellt und anschließend demontiert.

2.1. Maven-Konfiguration

Wir werden unsere REST-Anwendung auf Jersey aufbauen - der Referenzimplementierung von JAX-RS. Diese Implementierung erfordert einige Abhängigkeiten:


    org.glassfish.jersey.containers
    jersey-container-servlet-core
    2.27


    org.glassfish.jersey.inject
    jersey-hk2
    2.27

Wir können die neuesten Versionen dieser Abhängigkeitenhere undhere finden.

Wir werden das Jetty Maven-Plugin verwenden, um eine Testumgebung einzurichten. This plugin starts a Jetty server during the pre-integration-test phase of the Maven build lifecycle, then stops it in the post-integration-test phase.

So konfigurieren wir das Jetty Maven-Plugin inpom.xml:


    org.eclipse.jetty
    jetty-maven-plugin
    9.4.11.v20180605
    
        
            8999
        
        quit
        9000
    
    
        
            start-jetty
            pre-integration-test
            
                start
            
        
        
            stop-jetty
            post-integration-test
            
                stop
            
        
    

Wenn der Jetty-Server gestartet wird, überwacht er Port8999. Die KonfigurationselementestopKey undstopPortwerden ausschließlich vomstop-Ziel des Plugins verwendet, und ihr Wert ist aus unserer Sicht nicht wichtig.

InHere finden Sie die neueste Version des Jetty Maven-Plugins.

Eine andere zu beachtende Sache ist, dass wir daspackaging-Element in derpom.xml-Datei aufwar setzen müssen, sonst kann das Jetty-Plugin den Server nicht starten:

war

2.2. Erstellen einer REST-Anwendung

Der Anwendungsendpunkt ist sehr einfach: Es wird eine Willkommensnachricht zurückgegeben, wenn eine GET-Anforderung das Kontextstammverzeichnis erreicht:

@Path("/")
public class RestEndpoint {
    @GET
    public String hello() {
        return "Welcome to example!";
    }
}

So registrieren wir die Endpunktklasse bei Jersey:

package com.example.maven.it;

import org.glassfish.jersey.server.ResourceConfig;

public class EndpointConfig extends ResourceConfig {
    public EndpointConfig() {
        register(RestEndpoint.class);
    }
}

Um den Jetty-Server auf unsere REST-Anwendung aufmerksam zu machen, können wir den Bereitstellungsdeskriptor eines klassischenweb.xmlverwenden:


    
        rest-servlet
        org.glassfish.jersey.servlet.ServletContainer
        
            javax.ws.rs.Application
            com.example.maven.it.EndpointConfig
        
    
    
        rest-servlet
        /*
    

This descriptor must be placed in the directory /src/main/webapp/WEB-INF, die vom Server erkannt werden sollen.

2.3. Clientseitiger Testcode

Alle Testklassen in den folgenden Abschnitten enthalten eine einzige Methode:

@Test
public void whenSendingGet_thenMessageIsReturned() throws IOException {
    String url = "http://localhost:8999";
    URLConnection connection = new URL(url).openConnection();
    try (InputStream response = connection.getInputStream();
      Scanner scanner = new Scanner(response)) {
        String responseBody = scanner.nextLine();
        assertEquals("Welcome to example!", responseBody);
    }
}

Wie wir sehen können, sendet diese Methode lediglich eine GET-Anforderung an die zuvor eingerichtete Webanwendung und überprüft die Antwort.

3. Integrationstests in Aktion

Ein wichtiger Punkt beim Testen der Integration ist, dasstest methods often take quite a long time to run.

Aus diesem Grund sollten wir Integrationstests vom Standard-Build-Lebenszyklus ausschließen, um zu verhindern, dass sie den gesamten Prozess bei jedem Erstellen eines Projekts verlangsamen.

A convenient way to separate integration tests is to use build profiles. Diese Art der Konfiguration ermöglicht es uns, Integrationstests nur bei Bedarf durchzuführen - durch Angabe eines geeigneten Profils.

In den folgenden Abschnitten konfigurieren wir alle Integrationstests mit Build-Profilen.

4. Testen mit dem Failsafe Plugin

Der einfachste Weg, Integrationstests durchzuführen, ist die Verwendung vonthe Maven failsafe plugin.

Standardmäßig führt das Mavensurefire-Plugin Unit-Tests während dertest-Phase aus, währendthe failsafe plugin runs integration tests in the integration-test phase.

Wir können Testklassen mit unterschiedlichen Mustern für diese Plugins benennen, um die beigefügten Tests separat abzurufen.

Die durchsurefire undfailsafe erzwungenen Standardbenennungskonventionen sind unterschiedlich. Daher müssen wir diese Konventionen nur befolgen, um Einheiten- und Integrationstests zu trennen.

Die Ausführung des Pluginssurefire umfasst alle Klassen, deren Name mitTest beginnt oder mitTest,Tests oderTestCase endet. Im Gegensatz dazu führt das Pluginfailsafe Testmethoden in Klassen aus, deren Name mitIT beginnt oder mitIT oderITCase endet.

InThis finden wir die Dokumentation zum Testeinschluss fürsurefire, undhere ist die fürfailsafe.

Fügen wir dasfailsafe-Plugin mit der Standardkonfiguration zum POM hinzu:


    failsafe
    
        
            
                maven-failsafe-plugin
                2.22.0
                
                    
                        
                            integration-test
                            verify
                        
                    
                
            
        
    

InThis link finden Sie die neueste Version desfailsafe-Plugins.

Mit der obigen Konfiguration wird die folgende Testmethode in der Phaseintegration-testausgeführt:

public class RestIT {
    // test method shown in subsection 2.3
}

Da der Jetty-Server in der Phasepre-integration-test gestartet und inpost-integration-test heruntergefahren wird, besteht der Test, den wir gerade gesehen haben, mit diesem Befehl:

mvn verify -Pfailsafe

Wir können die Benennungsmuster auch so anpassen, dass Klassen mit unterschiedlichen Namen enthalten sind:


    maven-failsafe-plugin
    2.22.0
    
        
            **/*RestIT
            **/RestITCase
        
    
    ...

5. Testen mit dem Surefire Plugin

Abgesehen vomfailsafe Plugin,we can also use the surefire plugin to execute unit and integration tests in different phases.

Nehmen wir an, wir möchten alle Integrationstests mit dem SuffixIntegrationTest benennen. Da das Pluginsurefire standardmäßig in der Phasetest Tests mit einem solchen Namen ausführt, müssen wir sie von der Standardausführung ausschließen:


    maven-surefire-plugin
    2.22.0
    
        
            **/*IntegrationTest
        
    

Die neueste Version dieses Plugins isthere.

Wir haben alle Testklassen mit einem Namen, der mitIntegrationTest endet, aus dem Build-Lebenszyklus genommen. Es ist Zeit, sie mit einem Profil zurückzusetzen:


    surefire
    
        
            
                maven-surefire-plugin
                2.22.0
                
                    
                        integration-test
                        
                            test
                        
                        
                            
                                none
                            
                            
                                **/*IntegrationTest
                            
                        
                    
                
            
        
    

Instead of binding the test goal of the surefire plugin to the test build phase, as usual, we bound it to the integration-test phase. Das Plugin wird dann während des Integrationstests aktiviert.

Beachten Sie, dass wir einexclude-Element aufnone setzen müssen, um den in der Basiskonfiguration angegebenen Ausschluss zu überschreiben.

Definieren wir nun eine Integrationstestklasse mit unserem Namensmuster:

public class RestIntegrationTest {
    // test method shown in subsection 2.3
}

Dieser Test wird mit dem folgenden Befehl ausgeführt:

mvn verify -Psurefire

6. Testen mit dem Cargo Plugin

Wir können dassurefire Plugin mit dem Mavencargo Plugin verwenden. Dieses Plugin bietet integrierte Unterstützung für eingebettete Server, die für Integrationstests sehr nützlich sind.

Weitere Details zu dieser Kombination finden Sie unterhere.

7. Testen mit@Categoryvon JUnit

Eine bequeme Möglichkeit, Tests selektiv auszuführen, besteht darin,the @Category annotation im JUnit 4-Framework zu nutzen. This annotation lets us exclude particular tests from unit testing, and include them in integration testing.

Zunächst benötigen wir eine Schnittstelle oder Klasse, um als Kategorie-ID zu fungieren:

package com.example.maven.it;

public interface Integration { }

Wir können dann eine Testklasse mit der Annotation@Categoryund der KennungIntegrationdekorieren:

@Category(Integration.class)
public class RestJUnitTest {
    // test method shown in subsection 2.3
}

Anstatt die Annotation@Categoryfür eine Testklasse zu deklarieren, können wir sie auch auf Methodenebene verwenden, um einzelne Testmethoden zu kategorisieren.

Das Ausschließen einer Kategorie aus der Erstellungsphase vontestist einfach:


    maven-surefire-plugin
    2.22.0
    
        com.example.maven.it.Integration
    

Das Einbeziehen der KategorieIntegrationin die Phaseintegration-testist ebenfalls unkompliziert:


    category
        
        
            
                maven-failsafe-plugin
                2.22.0
                
                    
                        **/*
                    
                    com.example.maven.it.Integration
                
                
                    
                        
                            integration-test
                            verify
                        
                    
                
            
        
    

Wir können jetzt Integrationstests mit einem Maven-Befehl ausführen:

mvn verify -Pcategory

8. Hinzufügen eines separaten Verzeichnisses für Integrationstests

Manchmal ist es wünschenswert, ein separates Verzeichnis für Integrationstests zu haben. Organizing tests this way allows us to entirely isolate integration tests from unit tests.

Wir können das Mavenbuild helperPlugin für diesen Zweck verwenden:


    org.codehaus.mojo
    build-helper-maven-plugin
    3.0.0
    
        
            add-integration-test-source
            generate-test-sources
            
                add-test-source
            
            
                
                    src/integration-test/java
                
            
        
    

InHere finden wir die neueste Version dieses Plugins.

Die Konfiguration, die wir gerade gesehen haben, fügt dem Build ein Testquellverzeichnis hinzu. Fügen wir diesem neuen Verzeichnis eine Klassendefinition hinzu:

public class RestITCase {
    // test method shown in subsection 2.3
}

Es ist Zeit, Integrationstests in dieser Klasse durchzuführen:

mvn verify -Pfailsafe

Das Mavenfailsafe-Plugin führt aufgrund der in Unterabschnitt 3.1 festgelegten Konfiguration Methoden in dieser Testklasse aus.

Ein Testquellenverzeichnis gehört oft zu einem Ressourcenverzeichnis. Wir können ein solches Verzeichnis in einem anderenexecution-Element zur Plugin-Konfiguration hinzufügen:


    ...
    
        add-integration-test-resource
        generate-test-resources
        
            add-test-resource
        
        
            
                
                    src/integration-test/resources
                
            
        
    

9. Fazit

In diesem Artikel wurde die Verwendung von Maven zum Ausführen von Integrationstests mit einem Jetty-Server beschrieben, wobei der Schwerpunkt auf der Konfiguration der Plugins Mavensurefire undfailsafe lag.

Den vollständigen Quellcode für dieses Tutorial finden Sie unterover on GitHub.