Integration tests with spring boot - ClassNotFoundException

Published on 16 Oct 2017

Seems like today is that day of the week. There was another pretty weird issue (before the logging configuration, namingly I had some problems with running integration tests with the maven-failsafe-plugin for Spring Boot 1.5.7. I have the 2.20 failsafe plugin configured, and I got this error:

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.acme.UserManagementServiceIT
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.376 s <<< FAILURE! - in com.acme.UserManagementServiceIT
[ERROR] initializationError(com.acme.UserManagementServiceIT)  Time elapsed: 0.008 s  <<< ERROR!
java.lang.NoClassDefFoundError: com/acme/persistence/entity/UserEntity
Caused by: java.lang.ClassNotFoundException: com.acme.persistence.entity.UserEntity

And I knew that the class was there, so I - and also my boss - just looked like dumb, because we couldn’t comprehend initially what could go wrong there.

Turns out, that the guys at Surefire made a change from 2.18.1 to 2.19, namingly that they load the JAR file itself, instead of using the folders in target/ especially the target/classes folder. In the same time, around 1.4 Spring also changed how they are packaging the applications, and now when you run the spring-boot-maven-plugin’s repackage goal, you’ll end up having a JAR structure like this:

- BOOT-INF
  |- classes
     |- com/acme/YourClass.class  
  |- lib
     |- a.jar
     |- b.jar
- META-INF
  |- maven/{groupId}/{artifactId}
  	 |- pom.xml
     |- pom.properties
  |- MANIFEST.MF
- org/springframework/boot/loader

But from this structure, the failsafe plugin has no chance to figure out what to load. Oh yeah, goody.

So, what’s the solution: Add the target/classes folder to the test classpath:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <configuration>
        <additionalClasspathElements>
            <additionalClasspathElement>${basedir}/target/classes</additionalClasspathElement>
        </additionalClasspathElements>
        <includes>
            <include>**/*IT.java</include>
        </includes>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
</plugin>

And everything works like a charm!

Happy coding!