Most developers know how painful is accidental, unnecessary run of tests using the application context. It may come in the context of Jenkins CI jobs, accidental build runs or just developing.

How to minimize the probability of unintended runs of slow tests?

Separating things

If your tests run slowly you could separate long, complex and not frequently used ones from those which are fast to speed up your local deployment.

Usually unit tests are fast and even if you run them by mistake, you won’t feel uncomfortable. Integration, contract and acceptance tests can take several minutes if the project is huge. That’s why it makes sense to separate integration and unit tests.

Project structure

Non-modular java project structure in src folder usually looks like this:

All unit and integration tests are in test source. Our build.gradle file looks like this:

plugins {
    id 'org.springframework.boot' version '2.1.3.RELEASE'
    id 'java'
}

apply plugin: 'groovy'
apply plugin: 'io.spring.dependency-management'

group = 'com.inspeerity'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.11'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'

    testRuntime("org.codehaus.groovy:groovy-all:2.5.2")

    testImplementation('org.springframework.boot:spring-boot-starter-test')
    testImplementation("org.spockframework:spock-spring:1.2-groovy-2.5")
}

Adding new test source directory

After creating integration/groovy directory in src, you may notice that IDE doesn’t recognize it as test sources directory:

We need to add another test source and resource directory to Gradle project.

Let’s add the following lines to the build.gradle file:

sourceSets {
    integration {
        groovy.srcDir "$projectDir/src/integration/groovy"
        resources.srcDir "$projectDir/src/integration/resources"
        compileClasspath += main.output + test.output
        runtimeClasspath += main.output + test.output
    }
}

After refreshing gradle project you’ll see that integration/groovy is treated as source:

We need to add dependencies to our integration tests sources:

configurations {
    integrationImplementation.extendsFrom testImplementation
    integrationRuntime.extendsFrom testRuntime
}

Since now you can move all your integration tests to proper folder, and all of them will have all dependencies which are defined for test task.

Don’t forget to change testImplementation to integrationImplementation for each dependency that is used only by integration tests.

Defining integration task

The last thing left is to define the integration test task, to work with sources defined in integration section:

task integrationTest(type: Test) {
    testClassesDirs = sourceSets.integration.output.classesDirs
    classpath = sourceSets.integration.runtimeClasspath
}

check.dependsOn integrationTest

The dependsOn line forces to run integrationTest task while running check task which is executed every build.

Conclusion

With this knowledge you can separately execute integration and unit tests using proper task name.

The next step you can do is to prepare separate Jenkins tasks that will:

  1. Execute unit tests after each push to avoid blocking Jenkins workers
  2. Run all tests before merge, to make sure everything works.

Would you like to talk with our experts about custom software solutions for your business?

4.2/5 - (78 votes)