web-dev-qa-db-fra.com

Maven - déploie une application Web sur Tomcat avant le test JUnit

J'ai une application Web qui fournit un service Web. Je souhaite effectuer des tests JUnit avec SoapUI pour vérifier si ce service fonctionne correctement.
Mais pour tester une application de service Web doit être déployée sur mon serveur Tomcat 7.

Je ne sais pas comment configurer maven pour construire war, puis le déployer sur Tomcat (idéalement: pour exécuter une instance distincte de Tomcat à cet effet), puis pour exécuter des tests JUnit.
.__ J'apprécierai toute aide.

J'utilise Maven 2.2.1

34
Michał Herman

Il existe un certain nombre d'écoles de pensée sur la manière de gérer ce type de test d'intégration avec Maven.

Je tiens à souligner que lorsque vous déployez une application sur un serveur d'applications, vous n'êtes plus dans le domaine du test unitaire. Étant donné que l'application entière est déployée dans un conteneur, vous testez l'intégration de ces deux composants.

Maintenant, il n’ya rien de mal à utiliser JUnit pour exécuter tests d’intégration} (bien qu’il y ait certaines limitations que vous pouvez rencontrer, par exemple tests unitaires ne devrait pas s’occuper de la séquence de tests individuels - en supposant vous les écrivez correctement - donc JUnit applique ceci en ne garantissant aucune séquence d'exécution ... avant Java 1.7, l'ordre d'exécution était accidentellement impliqué par l'ordre des méthodes de test dans une classe, mais ce n'était pas le cas. fait partie du contrat JUnit ... Certaines personnes passent à d'autres frameworks de test pour leurs tests intégration, par exemple TestNG, si elles trouvent le focus de test unitaire de JUnit gênant leur développement de test)

Il convient de garder à l'esprit que le cycle de vie Maven utilise la phase test pour l'exécution des tests unit.

En ce qui concerne les intégration tests, il existe deux écoles de pensée (et demi) sur la bonne façon de gérer les tests avec Maven.

Ecole 1 - Failsafe et integration-test/verify

Cette école de pensée utilise les phases après package pour démarrer un conteneur, exécuter les tests d'intégration, démonter le conteneur et enfin vérifier les résultats des tests et échouer lors de la construction en cas d'échec des tests.

NE JAMAIS JAMAIS EXÉCUTER mvn integration-test car cela ne démontera pas le conteneur correctement, chaque fois que vous pensez taper mvn integration-test, vous voulez taper mvn verify (oh look, il est plus facile et plus rapide de taper aussi ... bonus)

Donc, avec cela, vous procédez comme suit:

Pour des points supplémentaires, utilisez build-helper-maven-plugin: reserve-network-port lié à la phase validate pour vous assurer que le serveur de test est démarré sur un port réseau inutilisé, puis utilisez le filtrage des ressources en fonction de la testez les ressources pour transmettre le port aux tests ou utilisez une propriété système transmise par systemPropertyVariables pour que le numéro de port soit disponible pour les tests.

Avantages

  • Clean Maven build
  • Si les tests échouent, vous ne pouvez pas libérer le projet.
  • Peut déplacer les tests d'intégration dans un profil séparé (par convention appelée run-its) si les tests sont trop lents pour exécuter chaque génération.

Désavantages

  • Difficile d'exécuter les tests à partir d'un IDE. Tous les tests d'intégration commencent/finissent dans IT et bien que Maven sache exécuter des tests commençant/finissant avec Test avec Surefire et des tests commençant/finissant avec IT avec Failsafe, votre IDE ne le fait probablement pas. De plus, votre IDE ne va pas démarrer le conteneur pour vous, vous devez donc effectuer beaucoup de travail à la main pour exécuter les tests manuellement. 
  • Le débogage des tests nécessite potentiellement la connexion de deux débogueurs, par exemple. un pour déboguer l'application en cours d'exécution dans un conteneur et l'autre pour déboguer les cas de test .

    mvnDebug -Dmaven.failsafe.debug=true verify
    
  • Associe vos tests au processus de construction Maven.

Ecole 2 - module séparé

Cette école de pensée déplace les tests d'intégration dans un module distinct qui dépend du module war et copie la war dans les ressources de test à l'aide, par exemple, de dependency:copy-dependencies lié à la phase generate-test-resources couplé à une dépendance Tomcat7 à tester.

Les cas de test démarrent le conteneur Tomcat7 en utilisant mode incorporé

Avantages

  • Les tests peuvent s'exécuter dans l'IDE
  • Les tests d'intégration étant séparés des tests unitaires, le fait de demander à IDE d'exécuter tous les tests ne lancera pas les tests plus lents.

Désavantages

  • L'artefact war n'est reconstruit que si vous dépassez la phase package. Par conséquent, vous devez exécuter au moins mvn clean package périodiquement pour actualiser le code sous test lors de l'utilisation de l'EDI.
  • L'échec des tests d'intégration ne rompt pas la construction du module war. Vous pouvez donc libérer un artefact war cassé puis faire échouer la construction du réacteur pour le module de test d'intégration. Certaines personnes s'opposent à ce problème en disposant le module de test d'intégration dans src/it et en utilisant Maven Invoker Plugin pour exécuter les tests ... bien que cela fournisse une intégration plus médiocre IDE, je ne recommande donc pas cette ligne.
  • Difficile d'obtenir un rapport de couverture de test consolidé de Maven.
  • Vous devez coder le conteneur démarrer/arrêter vous-même dans vos scénarios de test.

School 2.5 - Failsafe avec les cas de test démarrant leur propre serveur Tomcat7

C'est une sorte d'hybride des deux approches.Vous utilisez Failsafe pour exécuter les tests, mais ces derniers sont responsables du démarrage et de l'arrêt du conteneur Tomcat7 dans lequel vous souhaitez effectuer le test.

Avantages.

  • IDE peut exécuter tous les tests en toute sécurité (bien que les tests d'intégration puissent être plus lents et que vous souhaitiez peut-être ne pas les exécuter, mais ce n'est pas comme s'ils échoueraient tous sauf en cas d'échec du test).
  • Plus facile de déboguer les tests de votre IDE (un seul processus à attacher, et le IDE facilite généralement le débogage des tests en fournissant un programme d'exécution de test spécial)
  • Désavantages


J'espère que ce qui précède vous aide à comprendre les options que vous avez. Il peut y avoir d'autres modifications, mais en général, les solutions ci-dessus sont considérées comme les meilleures pratiques pour les tests d'intégration avec Maven à l'heure actuelle.

I hope the above helps you understand the options you have. There may be other tweaks but in general the above are considered the best practice(s) for integration testing with Maven at present.

115
Stephen Connolly

@Stephen Connolly - votre réponse ci-dessus était vraiment bonne. Je pensais que je mettrais en marche et montrerais une configuration complète pour ce que vous avez appelé une réponse School 1

Cette configuration:

  • exécute les tests unitaires séparément des tests d'intégration. Il utilise l'annotation @Category sur les classes racine étendues par les tests unitaires et les tests d'intégration.
  • avant les tests d'intégration, il lance l'application dépendante (chargée en tant que dépendance maven lors de l'exécution) sur la machine locale, en trouvant un port ouvert
  • après les tests d'intégration, il détruit l'application dépendante

Il y a d'autres choses ici comme comment définir certaines propriétés du système sur l'application dépendante uniquement.

Jusqu'à présent, cette configuration fonctionne à merveille ..

<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

Comme Stephen Connolly l'explique, il n'existe pas de moyen direct de configurer cela. Je vais vous expliquer comment résoudre ce problème en utilisant le plug-in Failsafe. Dans le cycle de vie Maven, le type de test peut être testé. L'unité de test l'un d'eux et un autre est le test d'intégration. Les tests unitaires peuvent être exécutés à l'étape du test du cycle de vie de maven. Lorsque vous souhaitez effectuer un test d’intégration, vous pouvez le faire en phase de vérification. Si vous voulez connaître la différence entre les tests unitaires et les tests intégrés, il s'agit d'un bon . Par défaut, les classes de tests unitaires doivent être au format ***/*Test.Java et **/*TestCase.Java de ce format. Le plug-in à sécurité intégrée recherchera **/IT*.Java, **/*IT.Java et **/*ITCase.Java

Voici un exemple. enter image description here

Ici, j'ai une classe de test unitaire et une classe de test intégrée. Maintenant, je vais expliquer, à quoi devrait ressembler le look de maven pom.xml. La section de construction de la configuration maven devrait ressembler à ceci. 

<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>

Les tests unitaires sont exécutés avant le déploiement de l'application Web (fichier war). Mais les tests intégrés sont exécutés en phase de vérification. J'espère que votre exigence est satisfaite à cette étape.

0
GPrathap