The JUnit Platform supports execution listeners. Such listeners are useful for hooking into the test run. In fact, both IntelliJ IDEA and Gradle use those for their purposes — like showing a relevant UI or generating a report. We can use them as well.
-
Declare the JUnit Platform Launcher dependency — this is where the
TestExecutionListener
is declared.testImplementation("org.junit.platform:junit-platform-launcher:{{JUnit Platform version}}")
-
Declare the listener. For example, at
src/test/kotlin/{{package path}}/Listener.kt
.package {{package name}} class Listener : TestExecutionListener { override fun testPlanExecutionStarted(testPlan: TestPlan) { println("Hello there") } }
-
Declare the listener at the
ServiceLoader
resource to let the JUnit Platform know about its existence. In our case it will be atsrc/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener
.{{package name}}.Listener
That’s it. Take a look at the TestExecutionListener
signature for different hooks.
It is possible to declare multiple listeners via appending entries to the resource above.
Mockito has memory management issues when using the inline mock maker. As a consequence, it is necessary to clear mocks manually after each test. Let’s automate this.
class MockitoListener : TestExecutionListener {
override fun executionFinished(testIdentifier: TestIdentifier, testExecutionResult: TestExecutionResult) {
Mockito.framework().clearInlineMocks()
}
}
Uncaught RxJava 2+ exceptions will not fail tests.
This happens because RxJava passes such exceptions to the UncaughtExceptionHandler
instead of thowing them.
The test engine doesn’t have a chance to record an exception and fail a test.
There is an interesting difference between platform UncaughtExceptionHandler
implementations. The JVM one will print a stacktrace, the Android one will stop the process.
This is fine but it is easy to expect that a test will fail on uncaught exceptions having an Android experience.
As a workaround, it is possible to use a custom UncaughtExceptionHandler
.
class RxJavaListener : TestExecutionListener {
override fun testPlanExecutionStarted(testPlan: TestPlan) {
Thread.setDefaultUncaughtExceptionHandler { _, e ->
throw AssertionError("Uncaught exception.", e)
}
}
}
The JUnit Jupiter engine supports parallel execution. The JUnit Platform provides APIs to make all engines parallel but it’s not so trivial, taking the nesting and the shared state into account. Plus it will not work well with the global-state-based tools like Mockito. Please open an issue describing the codebase and used testing tools and we’ll see what can be done in the future.
In the meanwhile, the Gradle parallel test execution is recommended.