Skip to content

Instantly share code, notes, and snippets.

@larryng
Created May 6, 2021 15:10
Show Gist options
  • Save larryng/c3814276d4b0f268db610433281fa2c0 to your computer and use it in GitHub Desktop.
Save larryng/c3814276d4b0f268db610433281fa2c0 to your computer and use it in GitHub Desktop.
allure-kotlin on Android 11

re: android/android-test#743 (comment)

The workaround is to write to the app external cache instead:

/**
 * Basically [AllureAndroidJUnitRunner] except:
 *  - [Allure.lifecycle] is set to [BniAllureLifecycle]
 *  - without [ExternalStoragePermissionsListener] which doesn't work on Android 11
 */
open class AndroidTestRunner : AndroidJUnitRunner() {

    override fun onCreate(arguments: Bundle) {
        Allure.lifecycle = BniAllureLifecycle
        val listenerArg = listOfNotNull(
            arguments.getCharSequence("listener"),
            AllureJunit4::class.java.name
        ).joinToString(separator = ",")
        arguments.putCharSequence("listener", listenerArg)
        super.onCreate(arguments)
    }
    // ...
}

/**
 * Custom [AllureLifecycle] that writes Allure files to app external cache to work around new
 * scoped storage limitations introduced in Android 11.
 */
object BniAllureLifecycle : AllureLifecycle(writer = BniAllureResultsWriter)

private object BniAllureResultsWriter : AllureResultsWriter {

    private var cachedContext: Context? = null
    private var cachedDelegate: AllureResultsWriter? = null

    private val delegate: AllureResultsWriter
        get() {
            val context = targetContext()
            return cachedDelegate
                ?.takeIf { context == cachedContext }
                ?: FileSystemResultsWriter(context.externalCacheDir!!.resolve("allure-results"))
                    .also {
                        cachedContext = context
                        cachedDelegate = it
                    }
        }

    override fun write(testResult: TestResult) {
        delegate.write(testResult)
    }

    override fun write(testResultContainer: TestResultContainer) {
        delegate.write(testResultContainer)
    }

    override fun write(source: String, attachment: InputStream) {
        delegate.write(source, attachment)
    }
}

Run with the custom AndroidTestRunner then afterwards pull results from $(adb shell echo \$EXTERNAL_STORAGE)/Android/data/YOUR_PACKAGE/cache/allure-results.

@NikitinSergii
Copy link

Hello Larry
I have faced the problem of running Instrumentation Espresso tests for Android 11 using AllureAndroidJUnitRunner.
I tried to use your solution but got a question. How can I import targetContext() function? Could I ask you to add used imports to this allure-kotlin-android-11.md file or provide the link to source code (if possible). Thank you in advance!

@larryng
Copy link
Author

larryng commented Mar 15, 2022

@NikitinSergii targetContext() should be

import androidx.test.platform.app.InstrumentationRegistry

InstrumentationRegistry.getInstrumentation().targetContext

@NikitinSergii
Copy link

NikitinSergii commented Mar 16, 2022

Hi Larry again
Yes, right.
But if I add
testInstrumentationRunner = "AndroidTestRunner"
to build.gradle file and write annotation @RunWith(AllureAndroidJUnit4::class) under test class, tests are not run.
Probably I made some mistakes, any advice?

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