3

I have been trying to convert our Integration tests to use the Maven Exec Plugin to start a server but it will only start the HTTP Server after the Integration tests have been executed, even though I have pre-integration-test specified.

Questions

Q1. Is there any other way to get the Integration tests to start after?

Q2. Is there a way to get the Integration phase to wait for the Server to start?

Q3. What's the difference between the Maven Exec and the Bazaar Maven Exec plugins?

Note: this worked previously using bazaar maven exec plugin but this is broken and can't seem to get it back working for JDK 11.0.9 on Jenkins.

The below is a snippet of our POM, we have various other plugins creating the jar and starting MockServer.

        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>exec</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <asyncDestroyOnShutdown>true</asyncDestroyOnShutdown>
                    <executable>java</executable>
                    <commandlineArgs>-Dserver.port=8089 -jar /gitRepos/public-api/target/api-jar-with-dependencies.jar</commandlineArgs>
                    <async>true</async>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.0.0-M5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <excludedGroups>${it.excluded}</excludedGroups>
                    <argLine>--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED</argLine>
                    <argLine>
                        --illegal-access=permit
                    </argLine>
                </configuration>
            </plugin>
          </plugins>


<!--            this what was previously working. -->
<!--            <plugin>-->
<!--                <groupId>com.bazaarvoice.maven.plugins</groupId>-->
<!--                <artifactId>process-exec-maven-plugin</artifactId>-->
<!--                <version>0.9</version>-->
<!--                <configuration>-->
<!--                    <processLogFile>${project.build.directory}/my-log.log</processLogFile>-->
<!--                </configuration>-->
<!--            </plugin>-->

The strange part for me is the HTTP Server starting after the Integration Tests, it's like other plugins are blocking the server from starting. See the logs below.

[DEBUG] Freed 4 thread-local buffer(s) from thread: nioEventLoopGroup-3-20
[DEBUG] Freed 4 thread-local buffer(s) from thread: nioEventLoopGroup-3-19
[DEBUG] Freed 5 thread-local buffer(s) from thread: nioEventLoopGroup-3-28
[INFO] [main] 13:13:37.594 [main] INFO  c.i.p.s.Server - Grizzly ThreadPool for listener grizzly set to 24 worker threads.
[INFO] [main] 13:13:37.724 [main] INFO  o.g.g.http.server.NetworkListener - Started listener bound to [0.0.0.0:8089]
[INFO] [main] 13:13:37.726 [main] INFO  o.g.grizzly.http.server.HttpServer - [HttpServer] Started.
M_K
  • 3,247
  • 6
  • 30
  • 47
  • To rule out the simple things first… according to the [exec-maven-plugin docs](https://www.mojohaus.org/exec-maven-plugin/plugin-info.html) `mainClass` is a valid element in the `configuration` of the `java` goal and not the `exec` goal you are using. Could you first try to fix this issue before we dig deeper into your (real) issue? – mle Feb 11 '21 at 20:29
  • @mle Sorry that must have been leftover from trying another config, it's commented out and I can confirm the same behaviour exists. – M_K Feb 12 '21 at 14:37

1 Answers1

2

Thanks for your the comment below your question.

After digging a little bit into your question I've decided to reproduce your suspicion and came to the conclusion that everything works as expected with the exec-maven-plugin.

Let me explain – I took your pom.xml snippet unchanged and implemented just a small Java main class which prints a line every 100 ms.

public class Ocp {
    public static void main(String[] args) {
        IntStream.range(1, 100).forEach(i -> {
            System.err.println("====== " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

The result is the following:

~/dev/git/so-mvn$ mvn clean verify
[INFO] Scanning for projects...
[INFO] 
[INFO] ---------------------------< de.mle:so-mvn >----------------------------
[INFO] Building stackoverflow 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 

***intentionally left out***

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ so-mvn ---
[INFO] Building jar: /home/marco/dev/git/so-mvn/target/so-mvn-0.0.1-SNAPSHOT.jar
[INFO] 
[INFO] --- exec-maven-plugin:3.0.0:exec (exec) @ so-mvn ---
[INFO] 
[INFO] --- maven-failsafe-plugin:3.0.0-M5:integration-test (default) @ so-mvn ---
====== 1
====== 2
====== 3
====== 4
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
====== 5
====== 6
====== 7
====== 8
[INFO] Running so.mvn.Ocp11IT
====== 9
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.057 s - in so.mvn.Ocp11IT
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-failsafe-plugin:3.0.0-M5:verify (default) @ so-mvn ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.398 s
[INFO] Finished at: 2021-02-12T19:17:24+01:00
[INFO] ------------------------------------------------------------------------

As you can see, the process starts clearly before the integration test phase (Q1 & Q2). Only due to your async config, the integration tests simply do not wait for my stub server to finish starting up. So everything fine with Maven here.

Your mentioned bazaarvoice plugin just did a little trick to mitigate this. It has two features which wait a little bit until they give their "go" for the integration tests. They are:

  • healthcheckUrl: Recommended, but optional. You should provide a healthcheck url, so the plugin waits until the healthchecks are all green for your process. If not provided, the plugin waits for waitAfterLaunch seconds before moving on.
  • waitAfterLaunch: Optional. This specifies the maximum time in seconds to wait after launching the process. If healthcheckUrl is specified, then it will move on as soon as the health checks pass. Default is 30 seconds.

Summary In your case, your bazaarvoice plugin just waited the default 30 seconds and that seems to be enough for your external server(s) to come up. There is no such "wait" feature in the exec-maven-plugin but you could e.g. move this yet missing health check programmatically in your integration tests itself (Q3), like in real life health checks for dependent (micro)services. A further option (my preferred one) is, to move all external server dependencies or processes in docker containers and then use again health check on the (Docker) plugin level.

A good starter for you could be if you take a look in my sample project with its little wait for an ES container to come up and the corresponding integration test.

Yet another option for you that comes without any waiting time at plugin level is the really sophisticated Awaitility lib which comes with nice waiting features or polling intervals right within your assertions self.

mle
  • 2,466
  • 1
  • 19
  • 25
  • Thanks for your answers, they have helped me and much appreciated. I don't think Docker is an option right now. I am currently trying out a health check in the Mock Server Maven plugins `ExpectationInitializer` class to slow down the starting of the tests, I'm not sure if this will be a viable option for us but it seems to be what the bazaar plugin is doing also. – M_K Feb 16 '21 at 10:40
  • Yeah exactly this approach "by expectation" was meant by my sentence "move this yet missing health check programmatically in your integration tests itself". In other words: assert preconditions and only then, execute tests. Do you need any further assistance? – mle Feb 16 '21 at 10:50
  • I also amended a hint to Awaitility in my answer which comes rather handy for waiting/polling directly in your tests. This would also mitigate further connection issues between tests and mock servers. Let me know if I can do anything further for you or if this allows you to accept my answer. ;-) – mle Feb 16 '21 at 15:50
  • 1
    Thanks, I got the IT tests working locally with the delay in the above mock server class , still need to try it on the Jenkins server, I will have a look at Awaitility also. – M_K Feb 17 '21 at 23:46
  • you're definitely right about the slow server (prob a combination of my laptop aswell), I uploaded it to Jenkins, and interestingly most of the time the Jenkins ci server is fine without the thread sleep, but I will still implement a health check just to cover developers running locally, thanks again for your help understanding these plugins. – M_K Feb 18 '21 at 13:39