I have webapp which provides web service. I want to perform JUnit tests with SoapUI to check if this service is working properly.
But to test web service application has
There are a number of schools of thought as to how to handle this type of integration test with Maven.
I should point out that when you are deploying an application to an application server, you are not in the realm of unit testing any more. Because the entire application is being deployed within a container, you are testing the integration of those two components.
Now there is nothing wrong with using JUnit for running integration tests (though there are some limitations that you may hit, for example unit tests should not care about the sequencing of individual tests - assuming you are writing them correctly - so JUnit enforces this by not guaranteeing any sequence of execution... prior to Java 1.7 the execution order was accidentally implied by the order of test methods within a class, but it was not part of the JUnit contract... Some people switch to other testing frameworks for their integration tests, e.g. TestNG, if they find the unit test focus of JUnit is getting in the way of their test development)
The key point to keep in mind is that the Maven lifecycle uses the test
phase for the execution of unit tests.
When it comes to integration tests there are two (and a half) schools of thought as to the right way to handle the tests with Maven.
integration-test/verify
This school of thought uses the phases after package
to start up a container, run the integration tests, tear down the container, and finally check the test results and fail the build in the event of test failures.
NEVER EVER RUN mvn integration-test
as that will not tear down the container correctly, any time you think you want to type mvn integration-test
you actually want to type mvn verify
(oh look, it's shorter and easier to type also... bonus)
So with this you do the following:
pre-integration-test
phase with fork=true
integration-test
phasepost-integration-test
phaseverify
phase.For extra brownie points you would use build-helper-maven-plugin:reserve-network-port bound to the validate
phase to ensure that the test server is started on an unused network port and then either use resource filtering against the test resources to pass the port through to the tests or use a system property passed through systemPropertyVariables to make the port number available to the tests.
run-its
) if the tests are too slow to run every build.IT
and while Maven knows to run tests starting/ending in Test
with Surefire and run tests starting/ending in IT
with Failsafe, your IDE probably doesn't. Additionally, your IDE is not going to start the container for you, so you have to do a lot of work by hand to actually run the tests manually. Debugging the tests potentially requires attaching two debuggers, e.g. one to debug the application running in container and the other to debug the test cases.
mvnDebug -Dmaven.failsafe.debug=true verify
Couples your tests to the Maven build process.
This school of thought moves the integration tests into a separate module that depends on the war
module and copies the war
into the test resources using, e.g. dependency:copy-dependencies bound to the generate-test-resources
phase coupled with a Tomcat7 dependency to test against.
The test cases themselves start up the Tomcat7 container using embedded mode
war
artifact is only rebuilt if you go past the package
phase, consequently, you need to run at least mvn clean package
periodically to refresh the code under test when using the IDE.war
module, so you can end up releasing a broken war
artifact and then have the reactor build fail for the integration test module. Some people counteract this issue by having the integration test module within src/it
and using Maven Invoker Plugin to run the tests... though that provides a poorer IDE integration, so I do not recommend that line.This is a kind of hybrid of the two approaches.
You use Failsafe to execute the tests, but the tests themselves are responsible for starting and stopping the Tomcat7 container that you want to test in.
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.
@Stephen Connolly - your answer above was really good. I thought I'd kick in and show a full configuration for what you termed a School 1
response.
This configuration:
@Category
annotation on the root classes that unit tests and integration tests extend.There's other stuff in there like how to set certain system properties on the dependent application only.
So far this configuration is working awesome..
<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>
As Stephen Connolly explains there is no a direct way to configure this. I will explain how to solve this using failsafe plugin. In the maven lifecycle type of test can be tested. Unit testing one of them and another one is integrate testing. Unit testing can be run in the test stage of maven lifecycle. When you want to do integrate test it can be done in verify stage. If you want to know the difference between unit testing and integrate testing, this is a good one. By default unit test classes should be in ***/*Test.java
, and **/*TestCase.java
this format. The failsafe plugin will look for **/IT*.java
, **/*IT.java
, and **/*ITCase.java
.
Here is an example.
Here I have one unit test class and one integrate test class. Now I will explain, how should be the looks like maven pom.xml. Build section of maven configuration should look like this.
<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 are run before deploying the web app(war file). But integrate tests are run in verify stage. I hope your requirement is satisfied in this stage.