Filed under: Gradle, Java, Programming, Test automation, Tools, — Tags: Acceptance Test Driven Development, Acceptance testing, Acceptance tests, Automation, Separating tests — Thomas Sundberg — 2015-04-29
It is very convenient to run the unit tests separated from other, slower, tests. There are different ways to do this. One way is to have a separate module for the acceptance tests.
Separating the modules is acceptable in some cases. It is not acceptable in others. There is a simple way to separate the source code for the acceptance tests while keeping the it in the same project if you use Gradle. Separate the tests with different source sets.
Separation on source sets means that you will keep all the acceptance tests in another directory structure than the unit tests. If you use the usual separation of production code and test code, then all you want to do is to add a new source set that contains the acceptance tests.
The regular source code structure used by Gradle is a src
directory and then a main
directory for the production code and test
for the test code. This is a good separation. We will add
another directory under src
called acceptanceTest
that will contain the source code used
for acceptance testing the system.
The magic is in the Gradle build script. When you apply the java
plugin, Gradle defines sourceSets
.
It will read src/main
for the production code and src/test
for the test code.
To be able to add acceptance tests in the same module you need to add an additional source set and call it something describing.
This my exact strategy in the example below:
build.gradle
plugins { id 'java' } sourceSets { acceptanceTest { java.srcDir file('src/acceptanceTest/java') resources.srcDir file('src/acceptanceTest/resources') compileClasspath += main.output + test.output + configurations.testRuntime runtimeClasspath += output + compileClasspath } } task acceptanceTest(type: Test) { description = 'Runs the acceptance tests' group = 'verification' testClassesDir = sourceSets.acceptanceTest.output.classesDir classpath = sourceSets.acceptanceTest.runtimeClasspath reports.junitXml.destination = '$buildDir/acceptance-test-results' reports.html.destination = '$buildDir/reports/acceptanceTest' dependsOn(test) } check { dependsOn(acceptanceTest) }
Notice that you don't replace the current sourceSets
definition, you enhance it.
I want to have access to the output from the production and test code for the acceptance tests. The way to get that
access is to sum the output and place it in my compile classpath compileClasspath += main.output + test.output
+ configurations.testRuntime
The runtime class path for the acceptance tests should be the same as the compile class path and the result of the compilation of the acceptance tests.
The next thing is to add a specific acceptance test task so I can execute my acceptance test separately.
The description of the task is easy, write whatever you want here. Make sure that is descriptive though.
After that I want to add my task to a group that it should belong to. The group should be set to
verification
. This is strictly not necessary, but the task will be grouped properly when you execute
gradle tasks.
Defining the task as a test
task will give me access to the properties testClassesDir
and
classpath
. To connect them to my acceptance test stuff, I set them to sourceSets.sourceSets.output.output
and sourceSets.acceptanceTest.runtimeClasspath
.
Separating the execution reports are nice. I do that by setting reports.junitXml.destination
and reports.html.destination
to something different compared to the unit tests. This allows me to get specific reports for the acceptance tests.
The last thing I want to do is to include the acceptance tests in the task chain at a reasonable place. I want the
the acceptance tests to be executed after the unit tests. The unit tests are fast so executing them before the
slower acceptance tests is reasonable. The solution is to add a dependency from the acceptanceTest to the test task.
I want the acceptanceTest to be executed when check
is executed so I add a dependency from the check
task to the acceptanceTest task. This will give the desired execution order and I will always execute the acceptance
tests when check is executed.
That is all you have to do to separate the acceptance test from the unit tests and get a specific task that will execute the acceptance tests.
The result is that you now have the possibility to organize your code like this:
example |-- build.gradle `-- src |-- acceptanceTest | |-- java | `-- resources |-- main | |-- java | `-- resources `-- test |-- java `-- resources
I would like to thank Malin Ekholm, Johan Helmfrid and Alexandru Bolboaca for proof reading.