After we have finished this lesson, we:
- Can add custom test sets to our Gradle build.
- Know how we can compile and run integration tests which use Spock Framework.
The Requirements of Our Gradle Build
The requirements of our Gradle build are:
- Our integration tests must have a separate source directory. The src/integration-test/groovy directory must contain the source code of our integration tests.
- Our integration tests must have a separate resource directory. The src/integration-test/resources directory must contain the resources of our integration tests.
- We must be able to run either unit tests, integration tests, or all tests.
- If an integration test fails, our build must fail as well.
- Integration and unit tests must have different HTML report directories.
Let’s start by adding a new test set to our Gradle build.
Adding Test Sets to Our Gradle Build
We can add new test sets to our Gradle build by using the Gradle TestSets plugin. We can use this plugin if we are also using the java and/or groovy plugin. We can apply the Gradle TestSets plugin by following these steps:
First, we have to declare the dependencies of our build script by following these steps:
- Configure Gradle to use the Bintray’s JCenter Maven repository when it resolves the dependencies of our build script.
- Add the Gradle TestSets plugin dependency (version 1.4.2) to the
classpathconfiguration.
After we have configured the dependencies of our build script, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
Second, we have to apply the Gradle TestSets plugin. After we have done this, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
apply plugin: 'org.unbroken-dome.test-sets'
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
plugins {
id 'groovy'
id 'org.unbroken-dome.test-sets' version '1.4.2'
}
Additional Reading:
Next, we have to configure the source and resource directories of our integration tests.
Configuring the Source and Resource Directories of Our Integration Tests
We can configure the source and resource directories of our integration tests by following these steps:
First, we have to add a new test set called integrationTest to our Gradle build. After we have done this, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
integrationTest
}
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
This configuration adds a source set called integrationTest to our build. This means that the src/integrationTest/groovy directory contains the source code of our our integration tests and the src/integrationTest/resources directory contains the resources of our integration tests.
Second, we have to change the name of the root directory from integrationTest to integration-test. We can do this by specifying the value of the dirName configuration option.
After we have changed the name of the root directory, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
integrationTest { dirName = 'integration-test' }
}
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
Let’s move on and write one integration test.
Writing a Simple Integration Test
The GetMessageSpec class contains one feature method which ensures that the getMessage() method of the MessageService class returns the correct message. Also, because we want to configure the category of this specification class, we have to annotate it with the @Category annotation.
The source code of our specification class looks as follows:
import org.junit.experimental.categories.Category
import spock.lang.Specification
@Category(IntegrationTest.class)
class GetMessageSpec extends Specification {
def messageService = new MessageService()
def 'Get message'() {
expect: 'Should return the correct message'
println 'Integration test: should return the correct message'
messageService.getMessage() == 'Hello World!'
}
}
System.out only because it’s the easiest way to see which feature methods are run. You shouldn’t do this in a real software project.The IntegrationTest interface is a marker interface that identifies our integration tests. Its source code looks as follows:
interface IntegrationTest {
}
Next, we have to configure the task that runs our integration tests.
Configuring the Task That Runs Our Integration Test
When we added a test set called integrationTest to our build, the Gradle TestSets plugin created a task called integrationTest that runs our integration tests. However, because we need to fulfill the requirements of our Gradle build, we have to make some changes to its configuration. We can make these changes by following these steps:
First, we have to ensure that unit tests are run before integration tests and that integration tests are run when we invoke the build task. We can do this by following these steps:
- Ensure that our integration tests are run before the
checktask and that thechecktask fails the build if an integration test fails. - Ensure that our unit tests are run before our integration tests. This guarantees that our unit tests are run even if our integration tests fail.
After we have made these changes, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
integrationTest { dirName = 'integration-test' }
}
check.dependsOn integrationTest
integrationTest.mustRunAfter test
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
Second, we have to ensure that the HTML reports of unit and integration tests are written to separate report directories. If we don’t do this, the HTML reports of our unit and integration tests are created to the same report directory. In other words, if we run both unit and integration tests, we can see only the HTML report that contains the test results of our integration tests.
We can configure the report directory of a task whose type is Test by setting the value of the reports.html.destination configuration option. Because we want to use separate report directories, we have to append the name of invoked task to the reporting base directory.
After we have done this, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
integrationTest { dirName = 'integration-test' }
}
check.dependsOn integrationTest
integrationTest.mustRunAfter test
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
}
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
Third, we have to configure the integrationTest task by following these steps:
- Ensure that our integration tests are run by JUnit. Even though JUnit is enabled by default, we have to do this because we want to make changes to the JUnit configuration.
- Configure JUnit to run only integration tests that belong to the
IntegrationTestcategory. We can do this by setting the fully qualified class name of theIntegrationTestinterface as the value of theincludeCategoriesJUnit configuration option. - Ensure that Gradle prints the information that is written to
System.outorSystem.errwhen our integration tests are run. By the way, we have to do this only because our feature method writes information toSystem.out.
After we have configured the integrationTest task, our build.gradle file looks as follows:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath(
'org.unbroken-dome.gradle-plugins:gradle-testsets-plugin:1.4.2'
)
}
}
apply plugin: 'groovy'
apply plugin: 'org.unbroken-dome.test-sets'
testSets {
integrationTest { dirName = 'integration-test' }
}
check.dependsOn integrationTest
integrationTest.mustRunAfter test
tasks.withType(Test) {
reports.html.destination = file("${reporting.baseDir}/${name}")
}
repositories {
mavenCentral()
}
dependencies {
compile(
'org.slf4j:slf4j-api:1.7.25',
'org.slf4j:slf4j-log4j12:1.7.25',
'log4j:log4j:1.2.17'
)
testCompile(
'junit:junit:4.12',
'org.codehaus.groovy:groovy-all:2.4.11',
'org.spockframework:spock-core:1.1-groovy-2.4'
)
}
test {
useJUnit {
includeCategories 'com.testwithspring.master.UnitTest'
}
testLogging {
showStandardStreams = true
}
}
integrationTest {
useJUnit {
includeCategories 'com.testwithspring.master.IntegrationTest'
}
testLogging {
showStandardStreams = true
}
}
Let’s move on and find out how we can run our unit and integration tests.
Running Unit and Integration Tests
We can run our tests by using one of the following options:
First, if we want to run only unit tests, we can use one of these two options:
- We can run unit tests by running the command: gradle clean test at command prompt.
- If we want to run our build and exclude integration tests, we have to run the command: gradle clean build -x integrationTest at command prompt.
When we run our unit tests, we should see an output that looks as follows:
$ gradle clean test
> Task :test
com.testwithspring.master.MessageServiceSpec > Get message STANDARD_OUT
Unit test: should return the correct message
BUILD SUCCESSFUL in 7s
7 actionable tasks: 7 executed
Second, if we want to run only integration tests, we can use one of these two options:
- We can run integration tests by running the command: gradle clean integrationTest at command prompt.
- We can run our build and exclude unit tests by running the command: gradle clean build -x test at command prompt.
When we run our integration tests, we should see an output that looks as follows:
$ gradle clean integrationTest
> Task :integrationTest
com.testwithspring.master.GetMessageSpec > Get message STANDARD_OUT
Integration test: should return the correct message
BUILD SUCCESSFUL in 2s
7 actionable tasks: 7 executed
Third, if we want to run all tests, we can use one of these two options:
- We can run unit and integration tests by running the command: gradle clean test integrationTest at command prompt.
- We can run our build by running the command: gradle clean build at command prompt.
When we run all tests, we should see an output that looks as follows:
$ gradle clean build
> Task :test
com.testwithspring.master.MessageServiceSpec > Get message STANDARD_OUT
Unit test: should return the correct message
> Task :integrationTest
com.testwithspring.master.GetMessageSpec > Get message STANDARD_OUT
Integration test: should return the correct message
BUILD SUCCESSFUL in 3s
10 actionable tasks: 10 executed
Let’s summarize what we learned from this lesson.
Summary
This lesson has taught us three things:
- We can add custom test sets to our Gradle build by using the Gradle TestSets plugin.
- Because we use the Groovy plugin, the Gradle TestSets plugin allows us to compile and run integration tests which are written by using the Groovy programming language.
- When we add a new test set to our Gradle build, the Gradle TestSets plugin creates a task that runs the tests which belong to our custom test set.