Separating acceptance tests

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

Acknowledgements

I would like to thank Malin Ekholm, Johan Helmfrid and Alexandru Bolboaca for proof reading.

References



(less...)

Pages

About
Events
Why

Categories

Agile
Automation
BDD
Clean code
Continuous delivery
Continuous deployment
Continuous integration
Cucumber
Culture
Design
DevOps
Executable specification
Git
Gradle
Guice
J2EE
JUnit
Java
Javascript
Kubernetes
Linux
Load testing
Maven
Mockito
New developers
Pair programming
PicoContainer
Presentation
Programming
Public speaking
Quality
React
Recruiting
Requirements
Scala
Selenium
Software craftsmanship
Software development
Spring
TDD
Teaching
Technical debt
Test automation
Tools
Web
Windows
eXtreme Programming

Authors

Thomas Sundberg
Adrian Bolboaca

Archives

Meta

rss RSS