在 CI 中使用存储权限进行 Android UI 测试

Posted

技术标签:

【中文标题】在 CI 中使用存储权限进行 Android UI 测试【英文标题】:Android UI Tests with Storage Permissions in CI 【发布时间】:2018-10-24 19:36:57 【问题描述】:

我想在 CI(Jenkins 流水线)中的项目上运行 UI 测试和单元测试。 UI 测试要求图像和视频位于测试设备/模拟器上。在 UI 测试中,我请求存储读/写访问权限,以便我可以将一些资产转储到下载文件夹中,然后在测试套件结束时删除它们。

当我在 Jenkins (mac) 上运行测试时,未授予权限,没有媒体传输,并且我的所有测试都失败了。

该项目包含一个应用模块和两个内部库模块。

流水线步骤

构建

sh "./gradlew clean assembleRelease"

单元测试

sh "./gradlew testReleaseUnitTest"

界面测试

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

问题

1) CI 构建挂在隐式 assembleDebugAndroidTest 任务上

2) 如果我在计算机上的命令行上运行该任务,则会安装测试,但未授予读取/写入存储的权限,因此所有测试都因设备上没有任何预期内容而失败。

我尝试过的事情

我尝试只测试发布版本,但这显示了相同的问题 2。testBuildType "release" 我没有其他需要使用的权限

我如何授予权限

@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

我看到了一种短期解决方案,可以将我的所有媒体资产手动复制到模拟器中。不过好像不太对,这个应该可以自动化吧。

【问题讨论】:

您能否发布测试失败时显示的异常?您还确定可以在模拟器中创建文件吗?可能是在 sdcard/emulated 文件夹不可写的情况下创建了存储...尝试在模拟器中的 Chrome 中从网站下载文件以确保 @ahasbini 1) 由于设备上没有媒体,测试失败,因此此处的堆栈跟踪无济于事。 2)我能够手动将文件复制到设备(拖放),然后一切都“没问题” 我已经尝试过您尝试的方式(拖放)并找到要复制到/sdcard/Downloads的文件。您是否能够确认文件正在被复制到模拟器中?使用命令adb shellcd /sdcard/Downloads' in order to confirm that the files are there using ls -la` 命令进行确认会很有帮助。 是的,一旦我拖放到模拟器上,我就可以确认文件存在。但是,这不是我想要做的,这是一种解决方法。理想的解决方案是通过 UI 测试实现自动化。理想的步骤是:1)在测试之前授予权限并将媒体复制到模拟器。 2)运行测试。 3)从模拟器中删除媒体 这看起来更像是复制文件的代码的问题。这就是堆栈跟踪和相关代码的原因。而不是@Rule @JvmField,我通常使用@get:Rule 【参考方案1】:

它与我一起工作的方式是按照以下步骤操作:

在我的app 模块的AndroidManifest.xml 文件中添加了权限。

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

并已尝试执行以下检测的测试用例/类:

@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"))
    

两个测试用例都成功返回。将rootDir 更改为"/" 后,测试失败,因为/ 目录是预期的只读目录。

拥有两个测试用例背后的想法是测试测试可以在运行时检索到的不同ContexttestTargetContextPermission 使用应用程序 Context没有它的包名称或应用程序 ID 以 .test 结尾。虽然testInstrumentationPermission 使用检测应用程序Context,但确实 的包名称或应用程序ID 以.test 结尾。我认为需要在他们的每个AndroidManifests 中定义权限,但事实证明,仅为您的app AndroidManifest 定义权限也会自动为 Instrumentation 包提供相同的权限。

【讨论】:

以上是关于在 CI 中使用存储权限进行 Android UI 测试的主要内容,如果未能解决你的问题,请参考以下文章

Android中常见的权限

Android文件选择器 自动申请存储权限 适配安卓 4.4 ~ 13支持无root权限访问和操作Android/data和Android/obb目录

Android 外置SDCard读写权限总结

如何在 Flutter 中更改权限对话框 UI? [关闭]

Android中SharedPreferences存储

Android——Android10的分区存储(Scoped Storage)