Spring Boot and Gradle: Separating tests

In this post I will present 4 different approaches to separating unit tests from integration tests, so that they can be run independently of each other. Here’s the 4 different approaches:

Separation based on name patterns

Separation based on JUnit categories

Separation based on Spring’s @IfProfileValue

Separation based on different source directories

These approaches can easily be extended to apply to other test types as well (performance tests for example). Also, please note that:

Only one approach is specific to using Spring.

The remaining 3 approaches can just as well be used without Spring.

For each approach you will find a reference to a super simple GitHub based project. Consult the projects there to see the source code in its entirety and true surroundings. All projects are based on JUnit 4, Spring Boot 1.4 and Gradle.

The important thing to remember here is that the patterns must end with .class. I hope you won’t fall into the trap of forgetting that detail now…

So, this is easy. All driven from Gradle. However, if developers uses an invalid suffix by mistake, then please note that this will result in the test classes’ test cases not being run at all. A bit dangerous.

So, this is easy as well. And it is type safe – so it is not brittle with respect to different test class names. Although not super elegant: Now you have to declare weird marker interfaces – and remember to annotate your test cases accordingly by pointing to them from the @Category annotation.

Again you see the @IfProfileValue annotation. This time the value is different though: integrationtest. Also notice how the @SpringBoot test annotation is used here as a meta-annotation. Having it here means that we don’t have to use it on the test classes also (in addition to the @IntegrationTest annotation and the @RunWith annotation).

Notice how a system property is passed to the JVM – effectively activating either the @UnitTest or the @IntegrationTest annotations.

This approach is kind of like the one based on JUnit categories. But I think the test classes look a bit leaner. One minor issue, if at all, is that Spring is used for running the unit tests also. It means a minor initial overhead of a few seconds at most.

Separation based on different source directories

This approach expects you to place unit tests in src/test/java and integration tests in src/integrationTest/java. No modifications to the test classes at all – no custom annotations, no categories.

Here’s how it is defined with Gradle:

...
apply plugin: 'java'
apply plugin: 'spring-boot'
...
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
// So that we can use JUnit and the test dependencies pulled in by Spring Boot
// from 'src/test/java' as well as 'src/integrationTest/java':
testCompile('org.springframework.boot:spring-boot-starter-test')
}
sourceSets {
// So that we can place source code in 'src/integrationTest/java':
integrationTest {
java {
// So that we can compile against the business classes (GreeterService, ...):
compileClasspath += main.output
// So that we can compile against the libs used by the business classes:
compileClasspath += main.compileClasspath
// So that we can compile against the unit test classes (custom utilities fx):
compileClasspath += test.output
// So that we can compile against the libs used by the unit test classes (JUnit, Spring test support, ...):
compileClasspath += test.compileClasspath
// So that test- and business classes can be used at runtime:
runtimeClasspath += test.runtimeClasspath
}
}
}
task integrationTest(type: Test) {
// So that Gradle knows where the integration test classes are:
testClassesDir = sourceSets.integrationTest.output.classesDir
// So that Gradle knows which runtime class path to use:
classpath = sourceSets.integrationTest.runtimeClasspath
}

Notice the comments – they highlight the relevant parts for getting it right.

This approach introduces another source directory layout and hence forces you to physically separate integration test classes from unit test classes. From a conceptual level I think this is the nicest model. But to be completely honest: getting the Gradle script “right” wasn’t super easy. And I bet you can find variants of this out there that does something slightly different. Or at least looks different.

In retrospective

There are at least these 4 ways that you can choose between. Each of them works fine – so choose the one that is most meaningful to you and your team.

Latest Comments

There’s a 5th way I have used, which is to create a separate project for your integration tests. Let’s say we have a project called business-logic. That project contains the code under test in /src/main/java and unit tests in /src/test/java. Create another project called business-logic-integration-tests, which depends on the business-logic project, and contains integrations tests in /src/test/java. This makes it easy to use a different config (e.g. resource files) for unit tests and the integration tests. It also scales well to several different types of tests – e.g. create one “test-project” for integration tests, one for Selenium tests, one for perf-tests, etc.

More from Spring

If you are using the core Spring Framework (that is, not Spring Boot), then you may have encountered a problem where Spring doesn’t seem to completely ignore mocked beans in your tests: Perhaps Spring attempts to inject beans into them or run your @PostConstruct lifecycle methods. In this post I present that problem together with… Continue Reading

In this post I am going to show how to copy MDC data from Web threads to @Async threads using a brand new Spring Framework 4.3 feature: ThreadPoolTaskExecutor#setTaskDecorator() [set-task-decorator]. This is the end result: Notice the third and second last log lines: they have [userId:Duke] just left of the log level. The first line is… Continue Reading

In the context of my favorite framework, Spring Boot, I have recently started to explore the effect of using the Kotlin 1.1 programming language (as an alternative to Java 8). This post describes a few language features that may be interesting to a typical Java 8 developer. Also, I hope you will see that: Spring… Continue Reading

In this post I show how you can create a Spring Boot 1.5 application using Kotlin 1.1 (as opposed to typically Java 8 in these times). The example I’ve created is a typical “Hello World” example. I have chosen to implement a Spring MVC controller – and an awesome Spring Boot integration test. The Gradle… Continue Reading

In this post I show how you can add support for Prometheus in Spring Boot applications. Step 1 of 2: Add the Prometheus Spring Boot Starter You can get Prometheus support in Spring Boot applications via a so-called “starter”. Here is how to get that… It’s a one liner in Gradle: And a five liner… Continue Reading

In this post I show how you, from a Spring Boot application, can control the timezone used by Hibernate when it communicates timestamps with the database. This post is inspired by a very similar recent post by Vlad Mihalcea [1], Hibernate Developer Advocate at Redhat. It’s basically the same subject – but with a Spring Boot… Continue Reading