Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integration testing with client server application in Maven multi-modules project

I have a project composed of several modules that uses maven and is automatically uploaded and deployed to a Jenkins application that runs the build and its tests.

There is for example an API module, a server and a client.

Both client and server use the API as dependency to be able to work correctly. The client connects to the server's web-services through HTTP(s) in normal use.

To be able to function, the server needs to be run on Jetty.

I have unit testing that works and test the client by calling the server's functionality with mocked HTTP requests.

I would like to be able to do some integration testing, to test for example the HTTP (and HTTPS) connection between the 2 entities, testing for timeouts and such, and reproducing the unit testing without resorting to mocked request, to be closer to real use cases.

It seems like such a thing should be easy to do, but I can't find a way to do it simply and in a way that could be automated.

For example, testing manually in my IDE is done by clicking on "run war" on the server project, and "run tests" on my application.

Is there a way to reproduce this simple process in Jenkins so it is done automatically, by Maven configuration or even a Jenkins plugin ?

UPDATE :

I'm trying to make another module that would use the war resulting from the packaging of my server, and run it with Jetty. Since my server's Jetty configuration is quite complicated, using Spring and https configuration filtered by Maven, I've some problems making it work in my new module because of missing classes and relative paths not working in that context.

Is it possible to run that war like it would have been started in the other module without jumping through hoops of duplicating resources and other dirty tricks ?

For information, the specific Jetty war part of my pom.xml is :

<configuration>
                    <contextHandlers>
                        <contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
                            <war>${project.parent.basedir}/cmp-service/cmp-webapp/target/cmp-webapp-1.0-SNAPSHOT.war</war>
                            <contextPath>/cmp</contextPath>
                            <persistTempDirectory>true</persistTempDirectory>
                            <allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
                        </contextHandler>
                    </contextHandlers>
</configuration>

UPDATE 2 :

I managed to build a functioning module that starts jetty and run my integration test with this pom.xml.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>parent</artifactId>
        <groupId>com.project.test</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.project.test.integration-testing</groupId>

    <artifactId>integration-testing</artifactId>
    <packaging>jar</packaging>

    <name>integration-testing</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.3.0.M2</version>
                <configuration>
                    <contextHandlers>
                        <contextHandler implementation="org.eclipse.jetty.webapp.WebAppContext">
                            <war>
                                ${project.parent.basedir}/myserver/myservice/target/myservice-${project.parent.version}.war
                            </war>
                            <contextPath>/cmp</contextPath>
                            <persistTempDirectory>true</persistTempDirectory>
                            <allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
                        </contextHandler>
                    </contextHandlers>

                    <contextXml>
                        ${project.parent.basedir}/myserver/myservice/src/test/resources/jetty/jetty-context.xml
                    </contextXml>
                    <jettyXml>
                        ${project.parent.basedir}/myserver/myservice/src/test/resources/jetty/jetty.xml,${project.parent.basedir}/myserver/myservice/src/test/resources/jetty/jetty-ssl.xml,${project.parent.basedir}/myserver/myservice/src/test/resources/jetty/jetty-http.xml,${project.parent.basedir}/myserver/myservice/src/test/resources/jetty/jetty-https.xml
                    </jettyXml>
                    <systemProperties>
                        <systemProperty>
                            <name>scsf.configuration.file</name>
                            <value>
                                ${project.parent.basedir}/myserver/myservice/src/main/resources/config/cmpserver.properties
                            </value>
                        </systemProperty>
                        <systemProperty>
                            <name>org.eclipse.jetty.annotations.maxWait</name>
                            <value>180</value>
                        </systemProperty>
                    </systemProperties>
                    <systemPropertiesFile>
                        ${project.parent.basedir}/myserver/myservice/src/main/resources/config/cmpserver.properties
                    </systemPropertiesFile>
                    <daemon>true</daemon>

                    <stopKey>STOP</stopKey>
                    <stopPort>10001</stopPort>
                </configuration>
                <executions>
                    <execution>
                        <id>start-jetty</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                        <configuration>
                            <scanIntervalSeconds>0</scanIntervalSeconds>
                        </configuration>
                    </execution>
                    <execution>
                        <id>stop-jetty</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skipTests>true</skipTests>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>2.18.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
       ...
    </dependencies>
</project>

You need to add the dependency to your war as provided too.

But (there's always a but) there is a problem when running the verify goal. When I run verify in my integration-test module, it kind of works. But when I run the verify goal in the parent, which is supposed to call that exact same verify goal of my integration-test module (if I understand correctly how it works), some paths are treated differently and are resolved as, for example, parent/src/... instead of parent/integration-test/src...

It seems that when running my goal from the parent, the context of the execution changes and causes my application to break when looking for resources and such.

Is there something I didn't understand about how the whole process works and is there a way to make it work ?

UPDATE 3

I've resolved to hack my way into making all my paths into absolute paths, it works, but it doesn't explain that behavior.

like image 451
Jay Zus Avatar asked Jun 08 '15 12:06

Jay Zus


People also ask

How do I run an integration test in Maven?

The simplest way to run integration tests is to use the Maven failsafe plugin. By default, the Maven surefire plugin executes unit tests during the test phase, while the failsafe plugin runs integration tests in the integration-test phase.

What is integration testing with example in Java?

Integration testing is the second level of the software testing process comes after unit testing. In this testing, units or individual components of the software are tested in a group. The focus of the integration testing level is to expose defects at the time of interaction between integrated components or units.


1 Answers

You already figured out the basic steps yourself:

  • create a separate module for integration tests
  • start an application- or web-server (e.g.jetty) via a suitable maven-plugin (e.g. cargo, wildfly-maven-plugin, etc.) in the pre-integration-test phase
  • run tests via the maven-failsafe-plugin:integration-test mojo
  • verify results via the maven-failsafe-plugin:verify mojo

Stephen Connolly, a maven developer, already wrote a great answer about this topic on stackoverflow.

I don't know the jetty-maven-plugin very well, but it seems it doesn't support deploying an existing artifact. Specifying relative or absolute path is definitively not the way to go here. You should probably use cargo instead to deploy to jetty. The cargo configuration could look like this:

<configuration>
  <configuration>
    <type>runtime</type>
    <properties>
      <cargo.hostname>testserver</cargo.hostname>
      <cargo.servlet.port>8080</cargo.servlet.port>
    </properties>
  </configuration>
  <container>
    <containerId>jetty9x</containerId>
  </container>
  <deployables>
    <deployable>
      <groupId>your.group.id</groupId>
      <artifactId>your-war</artifactId>
      <type>war</type>
      <properties>
        <context>/cmp</context>
      </properties>
    </deployable>
  </deployables>
  <deployer>
    <type>remote</type>
  </deployer>
</configuration>

The cargo documentation has more details.

hth,
- martin

like image 92
Martin Höller Avatar answered Oct 17 '22 08:10

Martin Höller