Integration testing with Gradle


Unit testing works automatically using Gradle, but if you would like to have a separate set of integration tests you need to do a small exercise. Actually they don’t have to be integration tests at all. This guide shows you how to configure Gradle to use any kind of tests and run them independently from others. I will use Scala language here but the same works for any JVM language.

The goal

We are about to define a new Gradle task named itest which will run only tests implemented in a specific folder “src/itest/scala”. The standard built-in task test will work without any change running only tests in “src/test/scala” directory.

Standard Java/Scala Project

We will start with a standard Gradle Java or Scala project. The programming language doesn’t matter here. Typically the directory structure looks like this:

<project root>
  + src
    + main
      + scala
    + test
      + scala
  - build.gradle

Main source code (being tested) resides in “src/main/scala” and all unit tests are in “src/test/scala”.

Where to put integration test classes and how to name them?

We already know where our unit tests are. A good habit is to name them using by the class they test, followed by “Test” or “Spec” suffix. For example if the tested class is named “Miracle” then unit tests for it should go to a class named “MiracleSpec” (or MiracleTest if you like). It’s just a convention, nothing more.

We will use the same principle for integration tests but we will put them inside “src/itest/scala” directory and use “ITest” or “ISpec” suffix. This is also a convention, but it allows us to run them separately from unit tests.

Why a special directory and also a special name suffix?

I recommend to put integration tests physically to a different directory and also use a different naming pattern so that you can distinguish the tests from the rest of your code in many other cases.

For example if you package the whole application into a one big fat JAR and you want to run integration tests only. How would you do that? Some test runners support filtering by class/file name only. You would use “*ISpec” regular expression to achieve it.

Another example is that it is very convenient to right-click a directory in your favourite IDE (IntelliJ IDEA for example) and run all tests inside the directory. Also IDEA allows you to run tests by providing class name pattern which is the reason why I like to use different suffixes for integration and unit tests.

Example project structure

Imagine a Scala project with one implementation class named Fujara (an awesome Slovak musical instrument). Its unit tests are in FujaraSpec class and integration tests in FujaraISpec. Often we need some data for integration tests (itest-data.xml) or logging configuration (logback-test.xml) different from the main application logging configuration.

<project root>
  + src
    + itest
      + resources
        + com
          + buransky
            - itest-data.xml
        logback-test.xml
      + scala
        + com
          + buransky
            - FujaraISpec.scala
    + main
      + resources
        - logback.xml
      + scala
        + com
          + buransky
            - Fujara.scala
    + test
      + scala
        + com
          + buransky
            - FujaraSpec.scala
  - build.gradle

The build.gradle

I am using Gradle 2.4 but this solution has worked for older versions too. I am not going to provide complete build script, but only the parts relevant to this topic.

configurations {
  itestCompile.extendsFrom testCompile
  itestRuntime.extendsFrom testRuntime
}

sourceSets {
  itest {
    compileClasspath += main.output + test.output
    runtimeClasspath += main.output + test.output

    // You can add other directories to the classpath like this:
    //runtimeClasspath += files('src/itest/resources/com/buransky')

    // Use "java" if you don't use Scala as a programming language
    scala.srcDir file('src/itest/scala')
  }

  // This is just to trick IntelliJ IDEA to add integration test
  // resources to classpath when running integration tests from
  // the IDE. It's is not a good solution but I don't know about
  // a better one.
  test {
    resources.srcDir file('src/itest/resources')
  }
}

task itest(type: Test) {
  testClassesDir = sourceSets.itest.output.classesDir
  classpath = sourceSets.itest.runtimeClasspath

  // This is not needed, but I like to see which tests have run
  testLogging {
    events "passed", "skipped", "failed"
  }
}

Run integration tests

Now we should be able to run integration test simply by running “gradle itest” task. In our example it should run FujaraISpec only. To run unit tests in FujaraSpec, execute “gradle test”.

Define other test types

If you would like to use the same principle for functional tests, performance tests, acceptance tests, or whatever tests, just copy&paste the code above and replace “itest” with “ftest”, “ptest”, “atest”, “xtest”, …