Skip to content

Instantly share code, notes, and snippets.

@pyricau
Last active May 3, 2023 07:51
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pyricau/970b95a4757a99b26562fd95e146f38f to your computer and use it in GitHub Desktop.
Save pyricau/970b95a4757a99b26562fd95e146f38f to your computer and use it in GitHub Desktop.
package com.example
import android.app.Instrumentation
import android.os.Bundle
import android.util.Log
import androidx.test.internal.runner.listener.InstrumentationResultPrinter
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.runner.Description
import org.junit.runner.notification.RunListener
/**
* A JUnit [RunListener] that installs a default [Thread.UncaughtExceptionHandler] to detect crashes
* in instrumentation tests and report a failure status to [Instrumentation], before letting the
* default crash handling continue (which will terminate the process).
*
* All you need to do is add the following to the defaultConfig of build.gradle:
*
* ```
* testInstrumentationRunnerArgument "listener",
* "com.example.CrashingRunListener"
* ```
*
* Then you can run instrumentation tests via Gradle as usual.
*
* When running UI tests via adb, add a *listener* execution argument to the command line for running
* the UI tests:
* `-e listener com.example.CrashingRunListener`. The full command
* line should look something like this:
*
* ```
* adb shell am instrument \\
* -w com.android.foo/android.support.test.runner.AndroidJUnitRunner \\
* -e listener com.example.CrashingRunListener
* ```
*/
internal class CrashingRunListener : RunListener() {
@Volatile
private lateinit var bundle: Bundle
@Volatile
private var isTestRunning = false
override fun testRunStarted(description: Description) {
val defaultHandler = Thread.getDefaultUncaughtExceptionHandler()!!
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
if (isTestRunning) {
isTestRunning = false
reportTestFailure(
"Instrumentation test failed due to uncaught exception in thread [${thread.name}]:\n" +
Log.getStackTraceString(throwable)
)
}
defaultHandler.uncaughtException(thread, throwable)
}
}
override fun testStarted(description: Description) {
val testClass = description.className
val testName = description.methodName
bundle = Bundle()
bundle.putString(Instrumentation.REPORT_KEY_IDENTIFIER, CrashingRunListener::class.java.name)
bundle.putString(InstrumentationResultPrinter.REPORT_KEY_NAME_CLASS, testClass)
bundle.putString(InstrumentationResultPrinter.REPORT_KEY_NAME_TEST, testName)
isTestRunning = true
}
override fun testFinished(description: Description?) {
isTestRunning = false
}
/**
* Reports that the test has failed, with the provided [message].
*/
private fun reportTestFailure(message: String) {
bundle.putString(InstrumentationResultPrinter.REPORT_KEY_STACK, message)
InstrumentationRegistry.getInstrumentation()
.sendStatus(InstrumentationResultPrinter.REPORT_VALUE_RESULT_FAILURE, bundle)
}
}
@uberbinge
Copy link

Hi, thanks for the gist ❤️ we benefitted from it a lot. Today when we updated Espresso to 3.5.1 lint fails with:

Error: InstrumentationResultPrinter.REPORT_KEY_NAME_CLASS can only be accessed from within the same library (androidx.test:runner) [RestrictedApi]
bundle.putString(InstrumentationResultPrinter.REPORT_KEY_NAME_CLASS, testClass)

If you happen to face the same issue and updated your own codebase, would appreciate if you can update the gist whenever you can 🙇 🙏

@pyricau
Copy link
Author

pyricau commented Jan 23, 2023

@uberbinge : Friends don't let friends believe Lint lies ;) .

This still works just fine for us, Lint can go fork itself.

The right approach here would be for Espresso to add an API that supports adding a custom failure at the end of a successful test, without having to resort to a rule. Don't hesitate to file a ticket to the Espresso project.

They could also add support for failing tests when unhandled exceptions fire.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment