Android Marshmallow引入了新的权限管理机制,需要在运行时检查特定的权限,这意味着需要根据用户是否拒绝或允许访问提供不同的流程。
由于我们使用Espresso在应用程序上运行自动化UI测试,因此如何模拟或更新权限状态以测试不同情景呢?
Android Marshmallow引入了新的权限管理机制,需要在运行时检查特定的权限,这意味着需要根据用户是否拒绝或允许访问提供不同的流程。
由于我们使用Espresso在应用程序上运行自动化UI测试,因此如何模拟或更新权限状态以测试不同情景呢?
随着Android测试支持库1.0的新版本发布,现在你可以在测试中使用GrantPermissionRule来在开始任何测试之前授予权限。
@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION);
Kotlin 解决方案
@get:Rule var permissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION)
@get:Rule
必须使用,以避免java.lang.Exception: The @Rule 'permissionRule' must be public.
更多信息在此处。
被接受的答案实际上并没有测试权限对话框,它只是绕过了它。因此,如果权限对话框由于某种原因失败,你的测试将会出现错误的绿色结果。我鼓励实际点击“授予权限”按钮来测试整个应用程序的行为。
看看这个解决方案:
public static void allowPermissionsIfNeeded(String permissionNeeded) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !hasNeededPermission(permissionNeeded)) {
sleep(PERMISSIONS_DIALOG_DELAY);
UiDevice device = UiDevice.getInstance(getInstrumentation());
UiObject allowPermissions = device.findObject(new UiSelector()
.clickable(true)
.checkable(false)
.index(GRANT_BUTTON_INDEX));
if (allowPermissions.exists()) {
allowPermissions.click();
}
}
} catch (UiObjectNotFoundException e) {
System.out.println("There is no permissions dialog to interact with");
}
}
在此处找到整个班级:https://gist.github.com/rocboronat/65b1187a9fca9eabfebb5121d818a3c4
顺便说一句,由于这个答案很受欢迎,我们在Barista上添加了PermissionGranter
,它是我们的工具,比Espresso和UiAutomator更好用。请查看https://github.com/SchibstedSpain/Barista,因为我们将会每次发布都维护它。
PermissionGranter.allowPermissionsIfNeeded(PERMISSION_1_CONTACTS)
- Jorge Casariego当您的手机处于英语环境时,请尝试使用此静态方法:
private static void allowPermissionsIfNeeded() {
if (Build.VERSION.SDK_INT >= 23) {
UiDevice device = UiDevice.getInstance(getInstrumentation());
UiObject allowPermissions = device.findObject(new UiSelector().text("Allow"));
if (allowPermissions.exists()) {
try {
allowPermissions.click();
} catch (UiObjectNotFoundException e) {
Timber.e(e, "There is no permissions dialog to interact with ");
}
}
}
}
我在 这里找到了它。
您可以在运行测试之前授予权限,例如:
@Before
public void grantPhonePermission() {
// In M+, trying to call a number will trigger a runtime dialog. Make sure
// the permission is granted before running this test.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getInstrumentation().getUiAutomation().executeShellCommand(
"pm grant " + getTargetContext().getPackageName()
+ " android.permission.CALL_PHONE");
}
}
但你无法撤销。如果尝试执行pm reset-permissions
或pm revoke...
,进程将被终止。
@org.junit.AfterClass
或 @org.junit.BeforeClass
注释,并正确执行操作,就应该能够撤消权限。 - Nathan Relinepm grand
之后重新启动应用程序才能工作,但这在测试中是不可能的。 - allofmexadb shell
命令。至于点击对话框,Espresso 应该能够自行处理。问题是,在你运行测试并允许权限之后,下一次运行测试将会失败,因为设置已经持久化,对话框不会再次显示。 - argenkiwi@Before
public void grantPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getInstrumentation().getUiAutomation().executeShellCommand(
"pm grant " + getTargetContext().getPackageName()
+ " android.permission.CAMERA");
}
}
ESPRESSO更新
这一行代码立即授予grant方法中列出的所有权限。换句话说,该应用程序将被视为已经授予了这些权限 - 不再需要弹出对话框。
@Rule @JvmField
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION)
以及Gradle
dependencies {
...
testImplementation "junit:junit:4.12"
androidTestImplementation "com.android.support.test:runner:1.0.0"
androidTestImplementation "com.android.support.test.espresso:espresso-core:3.0.0"
...
}
参考资料:https://www.kotlindevelopment.com/runtime-permissions-espresso-done-right/
该文章介绍了如何在Android应用程序中正确地使用Espresso进行运行时权限测试。首先,需要确保在代码中启用权限请求。其次,需要编写一个自定义规则来允许必需的权限。最后,建议将Espresso测试分解成小型单元测试,以使测试更加可靠。如果您需要为单个测试或运行时设置权限而不是规则,则可以使用以下方法:
PermissionRequester().apply {
addPermissions(android.Manifest.permission.RECORD_AUDIO)
requestPermissions()
}
e.g.
@Test
fun openWithGrantedPermission_NavigatesHome() {
launchFragmentInContainer<PermissionsFragment>().onFragment {
setViewNavController(it.requireView(), mockNavController)
PermissionRequester().apply {
addPermissions(android.Manifest.permission.RECORD_AUDIO)
requestPermissions()
}
}
verify {
mockNavController.navigate(R.id.action_permissionsFragment_to_homeFragment)
}
}
@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA, android.Manifest.permission.ACCESS_FINE_LOCATION);
@Rule
public GrantPermissionRule permissionRule = GrantPermissionRule.grant(android.Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.CAMERA);