How to attach a RunListener to your JUnit 4 tests in Gradle

Currently, there’s no native Gradle facility that allows you to attach a JUnit 4 RunListener to your tests. There’s an open request to add this feature, but this has yet to be implemented. Until this feature is delivered, you can attach RunListener classes to your JUnit 4 tests with JUnit Foundation.

The JUnit Foundation library enables you to declare your listeners in a service provider configuration file, which are then attached automatically - regardless of execution environment. Details can be found here.

Gradle Configuration for JUnit Foundation

// build.gradle
...
apply plugin: 'maven'
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
    mavenLocal()
    mavenCentral()
    ...
}
dependencies {
    ...
    compile 'com.nordstrom.tools:junit-foundation:9.4.2'
}
ext {
    junitFoundation = configurations.compile.resolvedConfiguration.resolvedArtifacts.find { it.name == 'junit-foundation' }
}
test.doFirst {
    jvmArgs "-javaagent:${junitFoundation.file}"
}
test {
//  debug true
    // not required, but definitely useful
    testLogging.showStandardStreams = true
}

ServiceLoader provider configuration file

# src/main/resources/META-INF/services/org.junit.runner.notification.RunListener
com.example.MyRunListener

With this configuration, the listener implemented by MyRunListener will be automatically attached to the RunNotifier supplied to the run() method of JUnit runners. This feature eliminates behavioral differences between the various test execution environments like Maven, Gradle, and native IDE test runners.

Development on JUnit Foundation continues. Version 11.0.0 was published on 17 July, 2019. This release provides significant enhancements to support alternate technologies like Cucumber.

1 Like

New version 12.2.0 has just been published. Upgrades since version 11.0.0 include:

  • Support for rule-base global timeout management, coordinated with annotation-based timeout
  • Full support for the JUnitParams runner, including publication of invocation parameters
  • Lifecycle event service providers are now declared in a single shared configuration file

JUnit Foundation now has a new home: https://github.com/sbabcoc/JUnit-Foundation

The README documentation has been updated to show a greatly simplified Java agent idiom for standard projects, and now also shows the Java agent idiom for Android projects.

Gradle Configuration for JUnit Foundation

// build.gradle

// ...

apply plugin: 'maven'
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
    mavenLocal()
    mavenCentral()
    // ...
}
dependencies {
    // ...
    compile 'com.nordstrom.tools:junit-foundation:12.2.0'
}
test {
//  debug true
    jvmArgs "-javaagent:${classpath.find { it.name.contains('junit-foundation') }.absolutePath}"
    // not required, but definitely useful
    testLogging.showStandardStreams = true
}

Gradle configuration (Android Studio):

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.1"

    // ...

    testOptions {
        unitTests.all {
            jvmArgs "-javaagent:${classpath.find { it.name.contains('junit-foundation') }.absolutePath}"
        }
    }
}

dependencies {
    // ...
    testImplementation 'com.nordstrom.tools:junit-foundation:12.2.0'
}