Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Maven Spring Boot Plugin : how to run spring boot from another project

https://docs.spring.io/spring-boot/docs/current/maven-plugin/usage.html

I have a project, with 2 modules.

[Parent]  
|-pom.xml
|  [SpringBoot2App]  
|  |-pom.xml  
|  [test]  
|  |-pom.xml  (start goal here!)

I want to run integration tests (maven failsafe plugin) in a separate project which is another module.

Is is possible to configure the spring boot maven plugin to start/stop the child module, during the integration tests of the parent module?

I tried something like this without success

        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>

            <configuration>

                <mainClass>com.SimpleServiceApplication</mainClass>
                <classesDirectory>../SpringBoot2App/target/classes</classesDirectory>
                <folders>
                     <param>../SpringBoot2App/target/test-classes</param>
                </folders>

            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>start</goal>
                    </goals>
                    <phase>pre-integration-test</phase>
                </execution>
            </executions>
        </plugin>

Which does not work.

I also tried to add a "project" parameter after reading the source of the super class of the plugin https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-tools/spring-boot-maven-plugin/src/main/java/org/springframework/boot/maven/AbstractRunMojo.java

            <configuration> 
                <mainClass>com.SimpleServiceApplication</mainClass>
                <project>${project.parent.collectedProjects[0]}</project>
            </configuration>

This refers to the right project, as debug shows, but does not work either.

Please don't comment on the [0], I know the [0] is not clean and is a coupling that requires direct knowledge of parent pom module ordering.

I get a java.lang.NoClassDefFoundError on org/springframework/boot/SpringApplication

I added the starter-web project to the test pom.xml, same result

like image 756
Filip Avatar asked May 29 '18 02:05

Filip


2 Answers

I don't think it's possible to run integration tests against another module using spring-boot-maven-plugin, because the start goal doesn't seem to give you a means to resolve the application from the local repository or the Maven reactor, which is probably what you want. The project configuration property you've tried isn't designed to be overriden that way. Plugin executions should be configured using only the properties listed in the plugin goal docs.

Instead I think you've got at least two possible approaches:

  1. Use a different plugin for controlling the server; or
  2. Run the server directly from the tests in code.

Option 1

For this I think you need an approach that copies in the server artifact you want to run, together with something a bit more general for starting and stopping it, like cargo-maven2-plugin or process-exec-maven-plugin.

Just configure the repackage goal in the server artifact build:

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <excludeDevtools>true</excludeDevtools>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>repackage</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Then from the integration tests module, you could do something like:

<plugin>
    <artifactId>maven-dependency-plugin</artifactId>
    <executions>
        <execution>
            <id>copy-server-artifact</id>
            <goals>
                <goal>copy</goal>
            </goals>
            <phase>pre-integration-test</phase>
            <configuration>
                <artifactItems>
                    <artifactItem>
                        <groupId>${project.groupId}</groupId>
                        <artifactId>SpringBoot2App</artifactId>
                        <version>${project.version}</version>
                        <classifier>jar</classifier>
                        <outputDirectory>${project.build.directory}</outputDirectory>
                        <destFileName>app.jar</destFileName>
                    </artifactItem>
                </artifactItems>
            </configuration>
        </execution>
    </executions>
</plugin>

<plugin>
    <groupId>com.bazaarvoice.maven.plugins</groupId>
    <artifactId>process-exec-maven-plugin</artifactId>
    <version>0.7</version>
    <executions>
        <execution>
            <id>start-server</id>
            <phase>pre-integration-test</phase>
            <goals>
                <goal>start</goal>
            </goals>
            <configuration>
                <name>run-server</name>
                <waitForInterrupt>false</waitForInterrupt>
                <healthcheckUrl>http://localhost:8080</healthcheckUrl>
                <arguments>
                    <argument>java</argument>
                    <argument>-jar</argument>
                    <argument>${project.build.directory}/app.jar</argument>
                </arguments>
            </configuration>
        </execution>

        <execution>
            <id>stop-server</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>stop-all</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Option 2

Just declare a normal Maven dependency on the server artifact from the test artifact, and run the server's @SpringBootApplication class in a JUnit before hook or what have you, e.g.

private static ConfigurableApplicationContext context;

@BeforeClass
public static void setUp() {
    context = new SpringApplicationBuilder(SimpleServiceApplication.class).run();
}

@AfterClass
public static void tearDown() {
    if (context != null) {
        context.close();
    }
}

This may be sufficient for your needs.

like image 113
ryanp Avatar answered Nov 09 '22 17:11

ryanp


Solutions from RyanP totally fit the needs.

But, I did manage to make it work, out of luck I guess :)

  1. it required re-adding dependencies in the TEST module, that are needed to run the app. (spring-boot.starter-web in this case)

  2. adding the tests classes, required a very interesting syntax

So far the advantages are that I can run test on a running server, yet still mock a few services using a profile and the test-classes of the service.

Honestly, I will still try both solutions above, but just for the show, here's what I finally got working

        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>

            <executions>
                <execution>
                    <id>start-mocked-app</id>
                    <configuration>

                        <arguments>
                            <argument>--server.port=${tomcat.http.port}</argument>
                        </arguments>

                        <mainClass>xxx.TestApplication</mainClass>
                        <classesDirectory>../${api-module}/target/classes</classesDirectory>

                        <folders>
                            <!-- wow! notice the weird "./" rather than the expected "../" -->
                            <folder>./${api-module}/target/test-classes</folder>
                        </folders>


                        <profiles>
                            <profile>MOCKED</profile>
                        </profiles>

                    </configuration>
                    <goals>
                        <goal>start</goal>
                    </goals>
                    <phase>pre-integration-test</phase>
                </execution>

                <execution>
                    <id>stop</id>

                    <goals>
                        <goal>stop</goal>
                    </goals>
                    <phase>post-integration-test</phase>
                </execution>
            </executions>
        </plugin>

And...

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- ... -->
</dependencies>
like image 3
Filip Avatar answered Nov 09 '22 16:11

Filip