简体   繁体   中英

Sharing integration test in multi module Maven project

Consider a Maven project with three modules Common , ServiceA , ServiceB where both services depend on Common and produce a war that is deployed as separate microservice. For each service I'd like to run an integration test, that checks the service exposes a healthcheck endpoint on /health path.

@Test
open fun testHealthCheck() {
    // implicitly asserts that response status is 200
    perform("/health", method = RequestMethod.GET)
}

The only solution I could come up with is to duplicate this test into test packages of each service. However, that's not very DRY. I'd like this integration test to be defined in a single place (preferably in the Common module) but to be run during the integration tests phase of each service.

How can I achieve that?

There are several ways to achive that. Both of that have pros and cons.

Unpack shared tests

Maven can attach multiple artifacts to each modules (eg sources or tests, etc.). First step is to attach test classes of the shared module.

<build>
    <plugins>
        <!-- ... -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.2.0</version>
            <executions>
                <execution>
                    <goals>
                        <goal>test-jar</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
    <!-- ... -->
</build>

After this it's easy to use in an other project as a dependency:

<dependencies>
    <dependency>
        <groupId>io.github.zforgo.stackoverflow</groupId>
        <artifactId>shared-tests</artifactId>
        <version>0.1.0-SNAPSHOT</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
</dependencies>

In the dependent project or module shared tests should unpack.

<build>
    <plugins>
        <!-- ... -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>3.1.2</version>
            <executions>
                <execution>
                    <id>share-tests</id>
                    <phase>generate-test-sources</phase>
                    <goals>
                        <goal>unpack</goal>
                    </goals>
                    <configuration>
                        <artifactItems>
                            <artifactItem>
                                <groupId>io.github.zforgo.stackoverflow</groupId>
                                <artifactId>shared-tests</artifactId>
                                <version>0.1.0-SNAPSHOT</version>
                                <classifier>tests</classifier>
                                <outputDirectory>${project.build.testOutputDirectory}</outputDirectory>
                            </artifactItem>
                        </artifactItems>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
    <!-- ... -->
</build>

NOTE : the ArtifactItem ignores type attribute so the classifier attribute should used instead of type . There is an issue in maven-dependency-plugin issue tracking system. MDEP-732

During unit or integration test phase the unpacked classes will run.

[INFO] ---------------< io.github.zforgo.stackoverflow:project >---------------
[INFO] Building project 0.1.0-SNAPSHOT                                    [3/3]
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-dependency-plugin:3.1.2:unpack (share-tests) @ project ---
[INFO] Configured Artifact: io.github.zforgo.stackoverflow:shared-tests:tests:0.2.0-SNAPSHOT:jar
[INFO] Unpacking /work/source/stackoverflow/junit-test-sharing/multimodule-test-sharing/shared-tests/target/shared-tests-0.1.0-SNAPSHOT-tests.jar to /work/source/stackoverflow/junit-test-sharing/multimodule-test-sharing/project/target/test-classes with includes "" and excludes ""
[INFO] 
[INFO] --- maven-surefire-plugin:3.0.0-M5:test (default-test) @ project ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.github.zforgo.stackoverflow.shared.SharedUnitTest
>>>> THIS IS A SHARED UNIT TEST <<<<
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.044 s - in io.github.zforgo.stackoverflow.shared.SharedUnitTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] --- maven-failsafe-plugin:3.0.0-M5:integration-test (default) @ project ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running io.github.zforgo.stackoverflow.shared.SharedIntegrationTestIT
>>>> THIS IS A SHARED INTEGRATION TEST <<<<
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.005 s - in io.github.zforgo.stackoverflow.shared.SharedIntegrationTestIT
Pros Cons
Both unit and integration tests shared in one step. Needs a workaround cause of MDEP-732
Shared tests will be packed into dependent artifact. Prevent changes. Class duplication makes dependent test-jar bigger unnecessarily.
Inclusion and exclusion supported during unpack.

Using failsafe and surefire plugins' <dependenciesToScan> attribute (recommended)

First of all I'd like to say thanks to @khmarbaise for suggestion.

Fortunately both maven-surefire-plugin and maven-failsafe-plugin has <dependenciesToScan> configuration attribute. All those listed dependencies will be scanned for test classes to include and run. NOTE Listed artifacts must be <dependency> elements in the dependent project or module.

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>io.github.zforgo.stackoverflow</groupId>
        <artifactId>shared-tests-aggregator</artifactId>
        <version>0.2.0-SNAPSHOT</version>
    </parent>

    <artifactId>project</artifactId>

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>shared-tests</artifactId>
            <version>${project.version}</version>
            <type>test-jar</type>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
                <configuration>
                    <dependenciesToScan>
                        <dependency>${project.groupId}:shared-tests:test-jar</dependency>
                    </dependenciesToScan>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.0.0-M5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>integration-test</goal>
                            <goal>verify</goal>
                        </goals>
                        <configuration>
                            <dependenciesToScan>
                                <dependency>${project.groupId}:shared-tests:test-jar</dependency>
                            </dependenciesToScan>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

NOTE : maven-failsafe-plugin:integration-test goal will report false error messages to console if the same package is missing in the dependent project.

[INFO] 
[INFO] --- maven-failsafe-plugin:3.0.0-M5:integration-test (default) @ project ---
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[ERROR] WARNING: package io.github.zforgo.stackoverflow.shared not in project
[INFO] Running io.github.zforgo.stackoverflow.shared.SharedIntegrationTestIT
>>>> THIS IS A SHARED INTEGRATION TEST <<<<
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.034 s - in io.github.zforgo.stackoverflow.shared.SharedIntegrationTestIT
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
Pros Cons
Easy to separate unit- and integration test sharing Configuration must be duplicated when unit- and integration tests are shared
Shared tests wont copied, no class duplication Delusive [ERROR] messages in logs but build is success.
Responsible plugins will handle dependent test classes Possible configuration changes time to time, version to version. Always read the documentation

Using build-helper-maven-plugin

This MojoHaus plugin allows to use multiple source or test-source directories in one module or project.

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>io.github.zforgo.stackoverflow</groupId>
        <artifactId>shared-tests</artifactId>
        <version>0.1.0-SHAPSHOT</version>
    </parent>
    <artifactId>project</artifactId>

    <dependencies>
        <dependency>
            <groupId>io.github.zforgo.stackoverflow</groupId>
            <artifactId>shared-test</artifactId>
            <version>0.1.0-SHAPSHOT</version>
            <type>test-jar</type>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <execution>
                        <phase>generate-test-sources</phase>
                        <goals>
                            <goal>add-test-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.parent.basedir}/shared-test/src/test/java/com/example/demo/shared</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
Pros Cons
Works only with local folders. External artifacts not supported.
Folders are hardcoded. Maintenance issues.

In short there are more possibilities to share tests between projects and modules but it's your turn to chose the best one.

What about creating a new Maven module specifically for integration testing? This module can then depend on both ServiceA and ServiceB . Your tests can then be run as part of the integration tests phase of this new module.

You may want to use the Maven failsafe plugin for integration tests. This allows integration tests to run from a separate goal. The reason for doing that would be for Continuous Integration tools such as Jenkins, where you would want unit tests to run, but not integration tests because, for example, they depend on an external web service running. If that service was not running, your CI build would fail.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM