如何在Instrumentation(Espresso)测试前将测试数据文件上传至设备?
刚好我之前也遇到过类似的Instrumental测试文件部署问题,给你几个实用的解决方案,完美适配你的场景:
方案1:利用被测APK的私有目录(无需额外权限)
这是最推荐的方案,完全绕开外部存储权限问题——因为应用对自己的私有目录拥有完全读写权限,不需要申请任何外部存储相关权限。
步骤如下:
- 把你的测试数据文件放到测试模块的
src/androidTest/assets文件夹下(如果没有就新建一个) - 在测试代码中,通过
Instrumentation.getTargetContext()获取被测应用的上下文,这样就能直接操作它的私有文件目录 - 用AssetManager把测试文件从测试APK的assets复制到被测APK的私有目录里
代码示例(Kotlin):
import androidx.test.platform.app.InstrumentationRegistry import java.io.File import java.io.FileOutputStream @Test fun prepareTestFile() { val instrumentation = InstrumentationRegistry.getInstrumentation() // 获取被测应用的上下文,用于访问它的私有目录 val targetAppContext = instrumentation.targetContext // 获取测试APK的AssetManager,读取测试文件 val testAssetManager = instrumentation.context.assets // 要复制的测试文件名 val testFileName = "sample_data.pdf" // 目标路径:被测APK的私有文件目录 val targetFile = File(targetAppContext.filesDir, testFileName) // 执行复制操作 testAssetManager.open(testFileName).use { inputStream -> FileOutputStream(targetFile).use { outputStream -> inputStream.copyTo(outputStream) } } // 现在被测应用可以直接读取这个文件,无需任何权限 // 接下来调用被测应用的文件选择/处理逻辑即可 }
方案2:用adb命令手动/脚本推送文件
如果需要在测试前预先部署文件,或者测试场景需要文件在外部存储(比如用户手动选择的场景),可以用adb命令推送:
推送到外部公共目录(比如Download)
adb push your_test_file.txt /sdcard/Download/
注意:Android 10+ 访问公共目录需要
MANAGE_EXTERNAL_STORAGE权限,但如果你的测试场景是模拟用户通过**存储访问框架(SAF)**选择文件,那么不需要权限——因为SAF是用户授权的,被测应用可以正常读取用户选择的文件。
推送到被测APK的私有目录(无需root,仅适用于debug包)
如果不想用外部存储,又想直接把文件放到被测APK的私有目录,可以用run-as命令:
# 先把文件推送到临时目录 adb push your_test_file.txt /sdcard/ # 切换到被测应用的身份,把文件复制到它的私有文件目录 adb shell run-as com.your.target.app.package cp /sdcard/your_test_file.txt files/
把com.your.target.app.package替换成你被测应用的包名即可。
方案3:封装Test Rule自动化文件部署
如果多个测试用例都需要部署相同的测试文件,可以封装一个Test Rule,让它在每个测试用例执行前自动完成文件复制,减少重复代码:
import androidx.test.platform.app.InstrumentationRegistry import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement import java.io.File import java.io.FileOutputStream class DeployTestFileRule(private val testFileName: String) : TestRule { override fun apply(base: Statement, description: Description): Statement { return object : Statement() { override fun evaluate() { // 执行文件复制逻辑 val instrumentation = InstrumentationRegistry.getInstrumentation() val targetAppContext = instrumentation.targetContext val testAssetManager = instrumentation.context.assets val targetFile = File(targetAppContext.filesDir, testFileName) testAssetManager.open(testFileName).use { input -> FileOutputStream(targetFile).use { output -> input.copyTo(output) } } // 执行原测试用例 base.evaluate() } } } }
然后在测试类中直接使用这个Rule:
import org.junit.Rule import org.junit.Test class FileProcessingTest { @get:Rule val deployTestFileRule = DeployTestFileRule("sample_data.pdf") @Test fun testFileProcessingFlow() { // 此时测试文件已经自动部署到被测APK的私有目录 // 直接调用被测应用的文件处理逻辑即可 val fileProcessor = FileProcessor() val result = fileProcessor.processFile("sample_data.pdf") // 断言处理结果符合预期 assert(result.isSuccess) } }
补充:为什么你之前的方案失败?
你之前尝试从测试APK的assets写入外部存储失败,是因为测试APK和被测APK的权限是相互独立的——即使测试APK申请了WRITE_EXTERNAL_STORAGE权限,被测APK没有这个权限的话,依然无法写入外部存储。而上面的方案1直接利用被测APK的私有目录,完全不需要外部存储权限,刚好解决这个问题。
内容的提问来源于stack exchange,提问作者engilyin
相关产品推荐
相关产品推荐

