Introduction
Recently when writing tests in java using gradle, I have stumbled upon a plugin called java-test-fixtures
When we write our tests, we always need to setup the relevant data and context for our tests. These usually involve setting a lot of entites and fields, which clutter the test logic. For this, we can use patterns like builders, factories or object mothers.
Test fixtures are commonly used to setup the code under test, or provide utilities aimed at facilitating the tests of a component.
The typical java application consists of two folders in the src/ directory: main/ and test/. What this plugin does, it adds a third testFixtures/ directory, where we can place our test fixutres.
We simply need to add the plugin into the build.gradle
plugins {
// A Java Library
id 'java-library'
// which produces test fixtures
id 'java-test-fixtures'
// and is published
id 'maven-publish'
}How it works
the java-test-fixtures plugin creates the following code structure
src/
├── main/
│ ├── java/
│ └── resources/
├── test/
│ ├── java/
│ └── resources/
└── testFixtures/
├── java/
└── resources/- the main module can see only its own source code
- tee test module can see both the main and testFixtures source code
- the testFixtures module can see only the main source code
The jar { enabled = false } problem
Now, as of writing this, one issue with the java-test-fixtures plugin that when I set jar { enabled = false } in my build.gradle,
I get a error: cannot find symbol exception for entities that definitely exist and work correctly when the plugin is not imported.
according to the Github issue, this was a design decision of the plugin to depend on the jar file directly instead of the source code.
The “fix”
Luckily, the plugin is quite simple for us to implement ourselves.
With just few modifications to the build.gradle we can implement custom sourceSets
sourceSets {
testFixtures {
java.srcDir 'src/testFixtures/java'
resources.srcDir 'src/testFixtures/resources'
compileClasspath += sourceSets.main.output
runtimeClasspath += sourceSets.main.output
}
test {
compileClasspath += sourceSets.testFixtures.output
runtimeClasspath += sourceSets.testFixtures.output
}
}
configurations {
testFixturesImplementation.extendsFrom testImplementation
testFixturesRuntimeOnly.extendsFrom testRuntimeOnly
}First, we define the testFixture sourceSet having the path src/testFixtures. Then we make the main source code accessible from the testFixtures.
Then, we extend the test sourceSet and add make the testFixtures source code visible to it.
Lastly, in the configurations we make testFixtures extend dependencies from test so we don’t have to specify most of the dependencies twice.