0

I have a maven project for a client library. I want to have my Maven build test that library against a real API using jUnit tests, and I want to stand the API up as part of my Maven build. I don't want the API to be on the classpath of my jUnit tests because my application uses Spring Framework's component scan and so additional things on the classpath could skew my results.

Is there a way that Maven could start my API with its own dependencies/classpath, run my jUnit tests with the test classpath of my module/project, then stop the API?

First Option I considered

Initially I thought I could just start the API from my jUnit tests, but the problem is that then I would need to include the API code and all its baggage in my test classpath, and I have a feeling this could skew my test results by bringing in dependencies and configuration that the library consumers won't necessarily have.

Second Option I considered

I also looked at using the spring boot plugin to start/stop the API using the test classpath on the pre- and post-integration phases, having the API as a "test"-scoped dependency, and using the failsafe plugin with some dependency exclusions to run my test methods using the library. However, this seems like a really complex way to do it and I'm having some trouble getting it working (namely, the start/stop executions can't seem to find the main class I specify for the API even though I configured it to use the test classpath). Here is what I have so far for that. Both the project with these plugins, and the clienttarget dependency are modules within another project.

<dependencies>
    <dependency>
        <groupId>com.example.test</groupId>
        <artifactId>clienttarget</artifactId>
        <version>3.0.LOCAL.RELEASE</version>
        <scope>test</scope>
    </dependency>
</dependencies>
<plugins>
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <mainClass>com.example.testapi.ClientTargetApiApplication</mainClass>
            <useTestClasspath>true</useTestClasspath>
        </configuration>
        <executions>
            <execution>
                <id>pre-integration-test</id>
                <goals>
                    <goal>start</goal>
                </goals>
            </execution>
            <execution>
                <id>post-integration-test</id>
                <goals>
                    <goal>stop</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>3.1.2</version>
        <configuration>
            <classpathDependencyExcludes>
                <classpathDependencyExclude>com.example.test:clienttarget</classpathDependencyExclude>
            </classpathDependencyExcludes>
            <excludedGroups>com.example.test.IntegrationTest</excludedGroups>
        </configuration>
    </plugin>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>3.1.2</version>
        <configuration>
            <classpathDependencyExcludes>com.example.test:clienttarget</classpathDependencyExcludes>
            <groups>com.example.test.IntegrationTest</groups>
        </configuration>
    </plugin>
</plugins>

Update: I've found a similar question, but it is not concerned with the classpath specifically, but instead with only having separate processes running at the same time: End to end integration test for multiple spring boot applications under Maven

snydergd
  • 503
  • 7
  • 11
  • Fork? You mean like multithreading test-execution? – Grim Jul 16 '23 at 16:09
  • @Grim Exactly -- by fork I really mean "run in the background, possibly as a separate process". I will happily accept suggestions for a better title, if you have any ideas. Thanks for asking! – snydergd Jul 16 '23 at 22:26
  • This looks like a different question. I have problems to understand your question. Have you concidered to split this question into two questions? – Grim Jul 17 '23 at 07:54
  • @Grim - I'm not sure how it could be split - do you see a good place to split it? I could maybe boil it down more. The question is really about how to run two isolated pieces of Java code - a client and a server - with their own classpaths as part of a Maven build. The API is only running to be a punching bag for jUnit tests which are the client. If that makes more sense, maybe I'll change the title to run along these lines. – snydergd Jul 18 '23 at 14:56
  • First things first, is it a different question? If not, please tell what excactly you mean by fork. Do the forked processes shall terminate when the maven build finishes? Or do the forked processes shall survive after maven build (like a deamon and terminate on pc-shutdown)? – Grim Jul 19 '23 at 09:39
  • @Grim I've only been talking about the one problem this whole time -- my comments and the post are about the same thing. If you're referencing the related link I added at the end - I think that that is a different question. I tried to summarize my question in my last comment. I'd consider splitting it into two, but don't see any place to split it. – snydergd Jul 19 '23 at 12:50
  • Again: Do the forked processes shall terminate when the maven build finishes? – Grim Jul 19 '23 at 12:53
  • 1
    @Grim Yes, they should. – snydergd Jul 19 '23 at 12:54
  • Why do you like to get rid of the test/compile classpath? – Grim Jul 19 '23 at 13:08
  • 1
    @Grim Thanks for your help. Spring does a component scan in my applications -- searching the whole classpath for classes annotated in certain ways and initializing them. Sometimes configuration from these even changes based on which classes are in the classpath (J2EE classes are a good example). This could prevent me from accurately testing my client code. At the very least, it still leaves the question of whether that is the problem or not, which would be one more thing to check for. – snydergd Jul 19 '23 at 14:46
  • Hm, since a canonical classname can be present only once in a classloader, you could replace classes from main with classes from test. As long as the canonical names are the same the test-class are preferred and the main-class is ignored. This way you could replace the component-scan from main-class by a different component-scan in test-class. – Grim Jul 19 '23 at 21:49
  • @Grim, my concern is that the API adds class X to the class path which is not on the classpath in my client library or unit tests. Class X causes some Spring configuration with `@ConditionalOnClass(X.class)` to be brought in. Depending on what other conditional configuration Spring autoconfiguration has like that, it could potentially change what implementation is handed to my client library code (e.g., maybe uses wagon or Commons HTTP client instead of HTTPUrlConnection -- maybe it switches my JAXB implementation). This makes it harder to accurately recreate real client scenarios. – snydergd Jul 19 '23 at 22:31
  • Do you know the canonical classname of the class having the `@ConditionalOnClass(X.class)` annotation? – Grim Jul 20 '23 at 07:40
  • @Grim I don't have a specific class in mind, and that isn't the only case I'm concerned about -- any class with `@Configuration`, `@Component`, or anything related on the classpath could cause these configuration changes. It wouldn't be obvious if there were a problematic one because the tests would likely still succeed -- or (the concern) may actually be successful when they will be unsuccessful in the real world. I don't want to risk it and also don't want to have that rabbit hole to go down when things behave weirdly for consumers. I want my test to run the same as actual consumers do. – snydergd Jul 20 '23 at 11:47

0 Answers0