it-swarm.com.de

Maven - Webapp für Tomcat vor dem JUnit-Test einsetzen

Ich habe Webapp, die Webservice anbietet. Ich möchte JUnit-Tests mit SoapUI durchführen, um zu prüfen, ob dieser Dienst ordnungsgemäß funktioniert.
Zum Testen der Webdienstanwendung muss auf meinem Tomcat 7-Server ein Deployment durchgeführt werden.

Ich habe keine Ahnung, wie ich Maven so konfigurieren sollte, dass es Krieg erstellt, und es dann in Tomcat bereitstellt (idealerweise: dafür eine separate Tomcat-Instanz ausführen) und dann JUnit-Tests ausführen.
Ich freue mich über jede Hilfe.

Ich benutze maven 2.2.1

34
Michał Herman

Es gibt eine Reihe von Überlegungen, wie mit dieser Art von Integrationstest mit Maven umgegangen werden soll.

Ich möchte darauf hinweisen, dass Sie sich beim Bereitstellen einer Anwendung auf einem Anwendungsserver nicht mehr im Bereich der Komponententests befinden. Da die gesamte Anwendung in einem Container bereitgestellt wird, testen Sie die Integration dieser beiden Komponenten.

Jetzt ist nichts falsch daran, JUnit zum Ausführen von Integrationstests zu verwenden (obwohl es einige Einschränkungen gibt, die Sie möglicherweise treffen, z. B. Unit-Tests ) sollte sich nicht um die Reihenfolge der einzelnen Tests kümmern - vorausgesetzt, Sie schreiben sie korrekt -, so dass JUnit dies erzwingt , indem es keine Ausführungsreihenfolge garantiert ... vor Java 1.7 Die Ausführungsreihenfolge wurde versehentlich durch die Reihenfolge der Testmethoden innerhalb einer Klasse impliziert, war jedoch nicht Teil des JUnit-Vertrags ... Einige Leute wechseln zu anderen Test-Frameworks für ihre Integration Tests, zB TestNG, wenn sie feststellen, dass der Unit-Test-Fokus von JUnit ihrer Testentwicklung im Wege steht)

Der wichtigste Punkt, den Sie berücksichtigen sollten, ist, dass der Maven-Lebenszyklus die test-Phase für die Ausführung von Unit-Tests verwendet.

Wenn es um Integrationstests geht, gibt es zweieinhalb Denkansätze, wie die Tests mit Maven richtig gehandhabt werden können.

Schule 1 - Ausfallsicher und _integration-test/verify_

Diese Denkschule verwendet die Phasen nach package, um einen Container zu starten, die Integrationstests auszuführen, den Container herunterzufahren und schließlich die Testergebnisse zu überprüfen und den Build bei Testfehlern fehlzuschlagen.

NIEMALS RUN _mvn integration-test_, da dies den Container nicht korrekt abreißt, wenn Sie denken, dass Sie _mvn integration-test_ eingeben möchten, möchten Sie tatsächlich _mvn verify_ (oh Sieh mal, es ist kürzer und einfacher zu tippen.

Damit machst du also folgendes:

Für zusätzliche Brownie-Punkte würden Sie Build-Helper-Maven-Plugin: Reserve-Network-Port verwenden, das an die validate-Phase gebunden ist, um sicherzustellen, dass der Testserver an einem nicht verwendeten Netzwerk-Port gestartet wird, und dann entweder die Ressource verwenden Filtern anhand der Testressourcen, um den Port an die Tests weiterzuleiten, oder Verwenden einer Systemeigenschaft, die an systemPropertyVariables übergeben wird, um die Portnummer für die Tests verfügbar zu machen.

Vorteile

  • Clean Maven bauen
  • Wenn die Tests fehlschlagen, können Sie das Projekt nicht freigeben
  • Kann die Integrationstests in ein separates Profil verschieben (laut Konvention _run-its_), wenn die Tests zu langsam sind, um jeden Build auszuführen.

Nachteile

  • Schwer, die Tests von einer IDE auszuführen. Alle Integrationstests beginnen/enden in IT und Maven kann Tests ausführen, die in Test mit Surefire beginnen/enden, und Tests ausführen, die in IT mit Failsafe beginnen/enden, Ihr IDE wahrscheinlich nicht. Darüber hinaus startet Ihr IDE den Container nicht für Sie, sodass Sie viel Handarbeit leisten müssen, um die Tests tatsächlich manuell auszuführen.
  • Das Debuggen der Tests erfordert möglicherweise das Anschließen von zwei Debuggern, z. eine zum Debuggen der im Container laufenden Anwendung und die andere zum Debuggen der Testfälle .

    _mvnDebug -Dmaven.failsafe.debug=true verify
    _
  • Koppelt Ihre Tests an den Maven-Build-Prozess.

Schule 2 - Separates Modul

Diese Denkschule verschiebt die Integrationstests in ein separates Modul, das vom war-Modul abhängt, und kopiert das war in die Testressourcen, indem z. dependency:copy-dependencies gebunden an die _generate-test-resources_ -Phase, gekoppelt mit einer Tomcat7-Abhängigkeit zum Testen.

Die Testfälle selbst starten den Tomcat7-Container mit eingebetteter Modus

Vorteile

  • Tests können in IDE ausgeführt werden
  • Integrationstests sind von Unit-Tests getrennt. Wenn Sie also die IDE auffordern, alle Tests auszuführen, werden die langsameren Tests nicht gestartet

Nachteile

  • Das Artefakt war wird nur dann neu erstellt, wenn Sie die Phase package hinter sich haben. Daher müssen Sie regelmäßig mindestens _mvn clean package_ ausführen, um den getesteten Code bei Verwendung der IDE zu aktualisieren.
  • Durch das Fehlschlagen der Integrationstests wird der Build des Moduls war nicht unterbrochen. Sie können also ein defektes war-Artefakt freigeben und den Reaktor-Build für das Integrationstestmodul fehlschlagen lassen. Einige Leute wirken diesem Problem entgegen, indem sie das Integrationstest-Modul in _src/it_ haben und das Maven Invoker-Plugin zum Ausführen der Tests verwenden. Dies bietet jedoch eine schlechtere IDE -Integration, weshalb ich diese Zeile nicht empfehle .
  • Es ist schwierig, einen konsolidierten Testbericht von Maven zu erhalten.
  • Sie müssen den Start/Stopp des Containers in Ihren Testfällen selbst codieren.

Schule 2.5 - Failsafe mit den Testfällen, die ihren eigenen Tomcat7-Server starten

Dies ist eine Art Hybrid aus beiden Ansätzen.

Sie verwenden Failsafe, um die Tests auszuführen, aber die Tests selbst sind für das Starten und Stoppen des Tomcat7-Containers verantwortlich, in dem Sie testen möchten.

Vorteile

  • Der Server muss nicht in Maven pom gestartet/gestoppt werden
  • IDE kann alle Tests sicher ausführen (obwohl die Integrationstests möglicherweise langsamer sind und Sie sie möglicherweise nicht ausführen möchten, aber es ist nicht so, als würden sie alle fehlschlagen, es sei denn, es liegt ein Testfehler vor).
  • Einfacheres Debuggen der Tests von Ihrem IDE (nur ein Prozess zum Anhängen, und der IDE vereinfacht normalerweise das Debuggen von Tests, indem ein spezieller Test-Runner bereitgestellt wird)

Nachteile

  • Sie müssen den Start/Stopp des Containers in Ihren Testfällen selbst codieren

Ich hoffe, dass Ihnen das oben Genannte hilft, die Optionen zu verstehen, die Sie haben. Möglicherweise gibt es andere Optimierungen, aber im Allgemeinen werden die oben genannten Methoden derzeit als Best Practice (s) für Integrationstests mit Maven angesehen.

118

@Stephen Connolly - Ihre Antwort war wirklich gut. Ich dachte, ich würde loslegen und eine vollständige Konfiguration für das zeigen, was Sie als School 1-Antwort bezeichnet haben. 

Diese Konfiguration:

  • führt Unit-Tests getrennt von Integrationstests aus. Es verwendet die Annotation @Category für die Stammklassen, die von Komponententests und Integrationstests erweitert werden.
  • vor Integrationstests wird die abhängige Anwendung (die zur Laufzeit als Mavenabhängigkeit geladen wird) auf dem lokalen Computer gestartet, indem ein offener Port gefunden wird
  • nach Integrationstests wird die abhängige Anwendung heruntergerissen

Es gibt noch andere Dinge wie das Festlegen bestimmter Systemeigenschaften nur für die abhängige Anwendung.

Bisher funktioniert diese Konfiguration hervorragend.

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>1.9.1</version>
            <executions>
                <execution>
                    <id>reserve-network-port</id>
                    <goals>
                        <goal>reserve-network-port</goal>
                    </goals>
                    <phase>pre-integration-test</phase>
                    <configuration>
                        <portNames>
                            <portName>Tomcat.maven.http.port</portName>
                        </portNames>
                    </configuration>
                </execution>
                <execution>
                    <id>get-local-ip</id>
                    <goals>
                        <goal>local-ip</goal>
                    </goals>
                    <configuration>
                        <!-- if not given, 'local.ip' name is used -->
                        <localIpProperty>local.ip</localIpProperty>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.Apache.Tomcat.maven</groupId>
            <artifactId>Tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <!-- http port from reserve-network-port-plugin-->
                <port>${Tomcat.maven.http.port}</port>
                <!-- application path always starts with /-->
                <path>/</path>
                <webapps>
                    <webapp>
                        <groupId>com.company.other.app</groupId>
                        <artifactId>web-rest</artifactId>
                        <version>1.0.1-SNAPSHOT</version>
                        <type>war</type>
                        <contextPath>/webapi-loopback</contextPath>
                        <asWebapp>true</asWebapp>
                    </webapp>
                </webapps>
            </configuration>
            <executions>
                <execution>
                    <id>start-server</id>
                    <configuration>
                        <fork>true</fork>
                        <skip>${skipTests}</skip>
                        <systemProperties>
                            <spring.profiles.active>test,h2</spring.profiles.active>
                        </systemProperties>
                    </configuration>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>run</goal>
                    </goals>
                </execution>
                <execution>
                    <id>stop-server</id>
                    <configuration>
                        <skip>${skipTests}</skip>
                    </configuration>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>shutdown</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.Apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.19</version>
            <configuration>
                <excludedGroups>com.company.app.service.IntegrationTestRootClassAnnotatedWithAtCategory</excludedGroups>
            </configuration>
            <executions>
                <execution>
                    <id>unit-test</id>
                    <phase>test</phase>
                    <goals>
                        <goal>test</goal>
                    </goals>
                    <configuration>
                        <argLine>-Xmx1024m -XX:MaxPermSize=256m @{jacocoArgLine}</argLine>
                        <excludedGroups> com.company.app.service.IntegrationTestRootClassAnnotatedWithAtCategory </excludedGroups>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.Apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.18</version>
            <dependencies>
                <dependency>
                    <groupId>org.Apache.maven.surefire</groupId>
                    <artifactId>surefire-junit47</artifactId>
                    <version>2.18</version>
                </dependency>
            </dependencies>
            <executions>
                <execution>
                    <id>start-integration-test</id>
                    <phase>integration-test</phase>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                    <configuration>
                        <argLine>-Xmx1024m -XX:MaxPermSize=256m @{jacocoArgLine}</argLine>
                        <groups>com.company.app.IntegrationTestRootClassAnnotatedWithAtCategory</groups>
                        <includes>
                            <include>**/*.Java</include>
                        </includes>
                        <systemPropertyVariables>
                            <program.service.url>
                                http://${local.ip}:${Tomcat.maven.http.port}/webapi-loopback
                            </program.service.url>
                        </systemPropertyVariables>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
1
Matt

Wie Stephen Connolly erklärt, gibt es keine direkte Möglichkeit, dies zu konfigurieren. Ich werde erklären, wie man dieses Problem mit dem fehlersicheren Plugin löst. Im Lebenszyklus des Maven-Lebenszyklus kann der Test getestet werden. Der Einheitentest von einem und dem anderen ist integriertes Testen. Unit-Tests können in der Testphase des Lebenszyklus von Maven durchgeführt werden. Wenn Sie den Test integrieren möchten, kann dies in der Überprüfungsphase erfolgen. Wenn Sie den Unterschied zwischen Unit-Tests und Integrations-Tests wissen möchten, ist dies ein guter Test . Standardmäßig sollten Komponententestklassen in ***/*Test.Java und **/*TestCase.Java dieses Format vorliegen. Das ausfallsichere Plugin sucht nach **/IT*.Java, **/*IT.Java und **/*ITCase.Java

Hier ist ein Beispiel. enter image description here

Hier habe ich eine Unit-Test-Klasse und eine Integrierte-Test-Klasse. Jetzt werde ich erklären, wie das aussehen soll wie maven pom.xml. Der Build-Abschnitt der Maven-Konfiguration sollte folgendermaßen aussehen. 

<build>
    <plugins>
        <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <webXml>src/main/webapp/WEB-INF/web.xml</webXml>
                <warName>${name}</warName>
                <outputDirectory>/home/jobs/wso2/wso2as-5.3.0/repository/deployment/server/webapps</outputDirectory>
                <goal>
                </goal>
            </configuration>
        </plugin>

        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.Apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.12.4</version>
            <executions>
                <execution>
                    <id>integration-test</id>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>

Unit-Tests werden vor der Bereitstellung der Web-App (War-Datei) ausgeführt. Integrierte Tests werden jedoch in der Überprüfungsphase ausgeführt. Ich hoffe, dass Ihre Anforderung in dieser Phase erfüllt ist.

0
GPrathap