Maven: despliegue webapp en tomcat antes de la prueba de JUnit
maven-2 tomcat7 (3)
Tengo una aplicación web que brinda servicio web. Quiero realizar pruebas JUnit con SoapUI para verificar si este servicio funciona correctamente.
Pero para probar la aplicación del servicio web debe implementarse en mi servidor Tomcat 7.
No tengo idea de cómo configurar maven para construir war, luego implementarlo en tomcat (idealmente: para ejecutar una instancia de tomcat separada para esto) y luego ejecutar pruebas de JUnit.
Apreciaré cualquier ayuda.
Estoy usando maven 2.2.1
@Stephen Connolly: su respuesta anterior fue realmente buena. Pensé en dar un puntapié y mostrar una configuración completa para lo que denominó una respuesta de la School 1
.
Esta configuración:
- ejecuta pruebas unitarias por separado de las pruebas de integración. Utiliza la anotación
@Category
en las clases raíz que las pruebas unitarias y las pruebas de integración amplían. - antes de las pruebas de integración, inicia la aplicación dependiente (cargada como una dependencia de maven en el tiempo de ejecución) en la máquina local, al encontrar un puerto abierto
- después de las pruebas de integración, elimina la aplicación dependiente
Hay otras cosas allí, como por ejemplo cómo configurar ciertas propiedades del sistema solo en la aplicación dependiente.
Hasta ahora, esta configuración funciona de maravilla.
<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>
Como explica Stephen Connolly, no hay una forma directa de configurar esto. Explicaré cómo resolver esto usando el plugin de failsafe. En el ciclo de vida de maven se puede probar el tipo de prueba. Las pruebas unitarias de uno y otro son pruebas integradas. Las pruebas unitarias se pueden ejecutar en la etapa de prueba del ciclo de vida de maven. Cuando quiera hacer una prueba integrada, puede hacerlo en la etapa de verificación. Si desea saber la diferencia entre las pruebas unitarias y las pruebas integradas, esta es una buena opción . Por defecto, las clases de prueba unitarias deben estar en ***/*Test.java
, y **/*TestCase.java
este formato. El plugin **/IT*.java
buscará **/IT*.java
, **/*IT.java
, y **/*ITCase.java
.
Aquí tengo una clase de prueba de unidad y una clase de prueba integrada. Ahora voy a explicar, ¿cómo debería ser el aspecto como maven pom.xml. La sección de compilación de la configuración de maven debería verse así.
<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>
Las pruebas unitarias se ejecutan antes de implementar la aplicación web (archivo war). Pero las pruebas integradas se ejecutan en la etapa de verificación. Espero que su requisito esté satisfecho en esta etapa.
Hay varias escuelas de pensamiento sobre cómo manejar este tipo de prueba de integración con Maven.
Debo señalar que cuando está implementando una aplicación en un servidor de aplicaciones, ya no está en el campo de las pruebas unitarias. Debido a que toda la aplicación se está implementando dentro de un contenedor, está probando la integración de esos dos componentes.
Ahora no tiene nada de malo utilizar JUnit para ejecutar pruebas de integración (aunque hay algunas limitaciones que puede aplicar, por ejemplo , las pruebas unitarias no deberían preocuparse por la secuencia de las pruebas individuales, suponiendo que las esté escribiendo correctamente), así que JUnit hace cumplir esto no garantizando cualquier secuencia de ejecución ... antes de Java 1.7 el orden de ejecución fue accidentalmente implícito por el orden de los métodos de prueba dentro de una clase, pero no era parte del contrato JUnit ... Algunas personas cambian a otros marcos de prueba para su integración pruebas, p. ej. TestNG, si encuentran que el foco de la unidad de prueba de JUnit está obstaculizando el desarrollo de sus pruebas)
El punto clave a tener en cuenta es que el ciclo de vida de Maven utiliza la fase de test
para la ejecución de pruebas unitarias .
Cuando se trata de pruebas de integración , hay dos (y media) escuelas de pensamiento sobre la forma correcta de manejar las pruebas con Maven.
Escuela 1 - Failsafe e integration-test/verify
Esta escuela de pensamiento utiliza las fases posteriores al package
para iniciar un contenedor, ejecutar las pruebas de integración, desmontar el contenedor y finalmente verificar los resultados de la prueba y fallar la compilación en caso de fallas en la prueba.
NUNCA EJECUTE mvn integration-test
ya que eso no destruirá el contenedor correctamente, cada vez que crea que desea escribir mvn integration-test
, realmente quiere escribir mvn verify
(oh look, es más corto y más fácil de escribir también ... prima)
Entonces con esto haces lo siguiente:
- Enlazar tomcat7:run a la fase de
pre-integration-test
confork
=true
- Enlazar a failsafe:integration-test a la fase de
integration-test
- Enlazar tomcat7:shutdown a la fase
post-integration-test
- Enlazar a prueba de fallas: verificar a la fase de
verify
.
Para obtener puntos extra brownie, usaría build-helper-maven-plugin:reserve-network-port vinculado a la fase de validate
para garantizar que el servidor de prueba se inicie en un puerto de red no utilizado y luego use el filtrado de recursos contra los recursos de prueba para aprobar el puerto a través de las pruebas o el uso de una propiedad del sistema pasa a través de systemPropertyVariables para que el número de puerto esté disponible para las pruebas.
Ventajas
- Clean Maven build
- Si las pruebas fallan, no puede lanzar el proyecto
- Puede mover las pruebas de integración a un perfil separado (por convención llamado
run-its
) si las pruebas son demasiado lentas para ejecutar cada compilación.
Desventajas
- Es difícil ejecutar las pruebas desde un IDE. Todas las pruebas de integración comienzan / finalizan en
IT
y aunque Maven sabe ejecutar pruebas que comienzan / finalizan enTest
con Surefire y ejecutan pruebas que comienzan / finalizan enIT
con Failsafe, es probable que su IDE no lo haga. Además, su IDE no va a iniciar el contenedor por usted, por lo que tiene que trabajar mucho a mano para ejecutar las pruebas de forma manual. La depuración de las pruebas posiblemente requiera la conexión de dos depuradores, por ejemplo, uno para depurar la aplicación que se ejecuta en el contenedor y el otro para depurar los casos de prueba .
mvnDebug -Dmaven.failsafe.debug=true verify
Combina tus pruebas con el proceso de construcción de Maven.
Escuela 2 - Módulo separado
Esta escuela de pensamiento mueve las pruebas de integración a un módulo separado que depende del módulo de war
y copia la war
en los recursos de prueba usando, por ejemplo, dependency:copy-dependencies
ligadas a la fase de generate-test-resources
junto con una dependencia de Tomcat7 para probar en contra.
Los casos de prueba se inician en el contenedor Tomcat7 utilizando el modo integrado
Ventajas
- Las pruebas se pueden ejecutar en IDE
- Las pruebas de integración están separadas de las pruebas unitarias, por lo que pedirle al IDE que ejecute todas las pruebas no iniciará las pruebas más lentas
Desventajas
- El artefacto de
war
solo se reconstruye si pasa la fase delpackage
, por lo tanto, debe ejecutar al menosmvn clean package
periódicamente para actualizar el código bajo prueba cuando se utiliza el IDE. - El fracaso de las pruebas de integración no rompe la construcción del módulo de
war
, por lo que puede terminar liberando un artefacto dewar
roto y luego hacer que la construcción del reactor falle para el módulo de prueba de integración. Algunas personas contrarrestan este problema al tener el módulo de prueba de integración dentro desrc/it
y usar Maven Invoker Plugin para ejecutar las pruebas ... aunque eso proporciona una integración IDE más pobre, por lo que no recomiendo esa línea. - Es difícil obtener un informe de cobertura de prueba consolidado de Maven.
- Tiene que codificar el inicio / detención del contenedor dentro de sus casos de prueba.
Escuela 2.5 - Fail safe con los casos de prueba iniciando su propio servidor Tomcat7
Este es un tipo de híbrido de los dos enfoques.
Utiliza Failsafe para ejecutar las pruebas, pero las pruebas en sí mismas son responsables de iniciar y detener el contenedor Tomcat7 en el que desea realizar la prueba.
Ventajas
- No es necesario configurar el inicio / detención del servidor en Maven pom
- IDE puede ejecutar todas las pruebas de forma segura (aunque las pruebas de integración pueden ser más lentas y es posible que no desee ejecutarlas, pero no todas fallarán a menos que exista una falla en la prueba)
- Es más fácil depurar las pruebas de su IDE (solo hay un proceso para adjuntar, y el IDE generalmente facilita la depuración de las pruebas al proporcionar un corredor de prueba especial)
Desventajas
- Tiene que codificar el inicio / detención del contenedor dentro de sus casos de prueba
Espero que lo anterior te ayude a entender las opciones que tienes. Puede haber otros ajustes pero, en general, lo anterior se considera la mejor práctica (es) para las pruebas de integración con Maven actualmente.