Integrationstests mit Maven

1. Übersicht

Maven ist das beliebteste Build-Tool im Java-Bereich, während Integrationstests ein wesentlicher Bestandteil des Entwicklungsprozesses sind. Daher ist es eine natürliche Wahl, Integrationstests mit Maven zu konfigurieren und auszuführen.

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 um Jersey herum erstellen - die 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 

Die neuesten Versionen dieser Abhängigkeiten finden Sie hier und hier.

Wir werden das Jetty Maven-Plugin verwenden, um eine Testumgebung einzurichten. Dieses Plugin startet einen Jetty-Server während der Testphase vor der Integration des Maven-Build-Lebenszyklus und stoppt ihn dann in der Testphase nach der Integration .

So konfigurieren wir das Jetty Maven-Plugin in pom.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 Port 8999 . Die Konfigurationselemente stopKey und stopPort werden ausschließlich vom Stoppziel des Plugins verwendet , und ihr Wert ist aus unserer Sicht nicht wichtig.

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

Eine andere Sache, die zu beachten ist, ist, dass wir das Verpackungselement in der Datei pom.xml auf war setzen müssen , sonst kann das Jetty-Plugin den Server nicht starten:

war

2.2. Erstellen einer REST-Anwendung

Der Anwendungsendpunkt ist sehr einfach: Er gibt eine Willkommensnachricht zurück, wenn eine GET-Anforderung den Kontextstamm erreicht:

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

So registrieren wir die Endpunktklasse bei Jersey:

package com.baeldung.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 einen klassischen Deployment-Deskriptor für web.xml verwenden :

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

Dieser Deskriptor muss sich im Verzeichnis / src / main / webapp / WEB-INF befinden , damit er vom Server erkannt wird.

2.3. Clientseitiger Testcode

Alle Testklassen in den folgenden Abschnitten enthalten eine einzige Methode:

@Test public void whenSendingGet_thenMessageIsReturned() throws IOException { String url = "//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 Baeldung!", 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

Bei Integrationstests ist zu beachten, dass die Ausführung von Testmethoden häufig sehr lange dauert.

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

Eine bequeme Möglichkeit, Integrationstests zu trennen, ist die Verwendung von Build-Profilen. 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 des ausfallsicheren Maven- Plugins.

In der Standardeinstellung der Maven todsicheren Plugin führt eine Unit - Tests während der Testphase, während der Failsafe - Plugin Integrationstests in der läuft Integration Testphase .

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

Die Standard-Namenskonventionen, die von todsicher und ausfallsicher erzwungen werden, sind unterschiedlich. Daher müssen wir diese Konventionen nur befolgen, um Einheiten- und Integrationstests zu trennen.

Die Ausführung des todsicheren Plugins umfasst alle Klassen, deren Name mit Test beginnt oder mit Test , Tests oder TestCase endet . Im Gegensatz dazu führt das ausfallsichere Plugin Testmethoden in Klassen aus, deren Name mit IT beginnt oder mit IT oder ITCase endet .

Hier finden wir die Dokumentation zur Testeinbeziehung für todsichere und hier die ausfallsichere Dokumentation .

Fügen wir das ausfallsichere Plugin mit der Standardkonfiguration zum POM hinzu:

 failsafe    maven-failsafe-plugin 2.22.0    integration-test verify       

Über diesen Link finden Sie die neueste Version des ausfallsicheren Plugins.

With the above configuration, the following test method will be executed in the integration-test phase:

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

Since the Jetty server starts up in the pre-integration-test phase and shuts down in post-integration-test, the test we have just seen passes with this command:

mvn verify -Pfailsafe

We can also customize the naming patterns to include classes with different names:

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

5. Testing With the Surefire Plugin

Apart from the failsafe plugin, we can also use the surefire plugin to execute unit and integration tests in different phases.

Let's assume we want to name all integration tests with the suffix IntegrationTest. Since the surefire plugin runs tests with such a name in the test phase by default, we need to exclude them from the default execution:

 maven-surefire-plugin 2.22.0   **/*IntegrationTest   

The latest version of this plugin is here.

We've taken all test classes having a name ending with IntegrationTest out of the build lifecycle. It's time to put them back with a profile:

 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. The plugin will then kick in during the integration testing process.

Notice that we must set an exclude element to none to override the exclusion specified in the base configuration.

Now, let's define an integration test class with our naming pattern:

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

This test will be running with the command:

mvn verify -Psurefire

6. Testing With the Cargo Plugin

We can use the surefire plugin with the Maven cargo plugin. This plugin comes with built-in support for embedded servers, which are very useful for integration testing.

More details about this combination can be found here.

7. Testing With JUnit's @Category

A convenient way to selectively execute tests is to leverage the @Category annotation in the JUnit 4 framework. This annotation lets us exclude particular tests from unit testing, and include them in integration testing.

First off, we need an interface or class to work as a category identifier:

package com.baeldung.maven.it; public interface Integration { }

We can then decorate a test class with the @Category annotation and Integration identifier:

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

Rather than declaring the @Category annotation on a test class, we can also use it at the method level to categorize individual test methods.

Excluding a category from the test build phase is simple:

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

Including the Integration category in the integration-test phase is also straightforward:

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

We can now run integration tests with a Maven command:

mvn verify -Pcategory

8. Adding a Separate Directory for Integration Tests

It's desirable at times to have a separate directory for integration tests. Organizing tests this way allows us to entirely isolate integration tests from unit tests.

We can use the Maven build helper plugin for this purpose:

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

Here is where we can find the latest version of this plugin.

The configuration we've just seen adds a test source directory to the build. Let's add a class definition to that new directory:

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

It's time to run integration tests in this class:

mvn verify -Pfailsafe

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

Ein Testquellverzeichnis gehört häufig zu einem Ressourcenverzeichnis. Wir können ein solches Verzeichnis in einem anderen Ausführungselement 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 todsicheren und ausfallsicheren Maven- Plugins lag.

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