Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android UI Tests with Storage Permissions in CI

I want to run UI tests and unit tests on a project in CI (Jenkins Pipeline). The UI tests requires images and video to be on the test device / emulator. In the UI tests I request permission for storage read/write access so I can dump a few assets into the downloads folder, then at the end of the test suite I remove them.

When I run my tests on Jenkins (mac) permissions are not granted, no media transfers over, and all of my tests fail.

The project contains an app module and two in-house library modules.

Pipeline steps

Build

sh "./gradlew clean assembleRelease"

Unit Test

sh "./gradlew testReleaseUnitTest"

UI Test

sh "$ANDROID_HOME/emulator/emulator @my_sweet_emulator_name -no-boot-anim & $ANDROID_HOME/platform-tools/adb wait-for-device"
sh './gradlew connectedAndroidTest'

Issues

1) The CI build hangs on the implicit assembleDebugAndroidTest task

2) If I run that task on the command line on my computer the tests will install however the permission is not granted to read/write storage so all tests fail due to not having any expected content on the device.

Things I've Tried

  • I have attempted only testing a release build however this shows the same issue 2. testBuildType "release"
  • I have no other permissions I need to work with

How I'm granting permissions

@RunWith(AndroidJUnit4::class)
class MyMediaClassTest {

    @Rule
    @JvmField
    val activityRule = ActivityTestRule(MainActivity::class.java)

    @Rule
    @JvmField
    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule
            .grant(android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
// tests and stuff
}

I have seen a short-term solution to manually copy all of my media assets into the emulator. But that doesn't seem right, this should be able to be automated.

like image 922
Sababado Avatar asked Oct 24 '18 19:10

Sababado


1 Answers

The way it worked with me is by following the below steps:

Added the permissions in the AndroidManifest.xml file of my app module.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

And have tried executing the below instrumented test cases/class:

@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {

    @Rule
    @JvmField
    val grantPermissionRule: GrantPermissionRule = GrantPermissionRule
            .grant(android.Manifest.permission.READ_EXTERNAL_STORAGE,
                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE)

    val rootDir = "/sdcard/"

    @Test
    fun testTargetContextPermission() {
        val targetContext = InstrumentationRegistry.getTargetContext()
        assertTrue("Not the target package",
                !targetContext.packageName.endsWith(".test"))
        assertTrue("Permissions not Granted", targetContext.checkSelfPermission(
                android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                targetContext.checkSelfPermission(
                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
        val file = File(rootDir + "targetTest.txt")
        if (file.exists()) {
            file.delete()
        }
        assertTrue("File was not created", file.createNewFile())
        val sdcard = File(rootDir)
        assertTrue("sdcard is empty or file not found", sdcard.list().size != 0 &&
                Arrays.asList<String>(*sdcard.list()).contains("targetTest.txt"))
    }

    @Test
    fun testInstrumentationPermission() {
        val instrumentationContext = InstrumentationRegistry.getContext()
        assertTrue("Not the instrumentation package",
                instrumentationContext.packageName.endsWith(".test"))
        assertTrue("Permissions not Granted", instrumentationContext.checkSelfPermission(
                android.Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                instrumentationContext.checkSelfPermission(
                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)
        val file = File(rootDir + "instrumentationTest.txt")
        if (file.exists()) {
            file.delete()
        }
        assertTrue("File was not created", file.createNewFile())
        val sdcard = File(rootDir)
        assertTrue("sdcard is empty or file not found", sdcard.list().size != 0 &&
                Arrays.asList<String>(*sdcard.list()).contains("instrumentationTest.txt"))
    }
}

Both test cases returned with success. Upon changing rootDir to "/", the tests failed since the / directory is a read-only which is expected.

The idea behind having two test cases is to test the different Context which the tests could retrieve at runtime. The testTargetContextPermission uses the app Context which does not have it's package name or application id ending with .test. While the testInstrumentationPermission uses the instrumentation app Context which does have it's package name or application id ending with .test. I thought that permissions need to be defined in each of their AndroidManifests but turns out that defining the permissions for your app AndroidManifest only will automatically give the same permissions to the Instrumentation package as well.

like image 88
ahasbini Avatar answered Oct 21 '22 09:10

ahasbini