Espresso测试中@Test方法内权限撤销操作未生效问题求助
搞定Espresso测试里权限撤销没执行的问题
问题根源拆解
我明白你现在的困境:在test4里授予了位置权限,想在test5里撤销,但运行时发现撤销方法根本没机会执行,测试流程直接结束了。而把撤销逻辑放在@BeforeClass或@AfterClass里却正常。
这主要有两个核心原因:
- JUnit默认不保证测试方法的执行顺序,你写的
test4和test5可能不是按你预期的顺序跑的,说不定test5先执行了,那自然没权限可撤销; - 测试方法的设计原则是原子独立,每个测试都要在干净的环境下运行,依赖前一个测试的状态本身就不符合规范,万一
test4抛出异常,后续的test5直接就被跳过了。
靠谱的解决方案
1. 用@After注解自动清理(最推荐)
把权限撤销逻辑放到@After注解的方法里,这样每个测试方法执行完后,不管成功还是失败,都会自动触发撤销操作,保证下一个测试的环境是干净的:
@After public void cleanUpPermissions() { revokePermission(); } @Test public void test4clickAllowLocation() { // 你的测试代码原样保留 context = getInstrumentation().getContext(); onView(withId(R.id.location_switch)).perform(scrollTo()).perform(click()); onView(isRoot()).perform(waitFor(3000)); onView(withId(R.id.second_Btn)).inRoot(isDialog()).perform(click()); allowPermissionsJustOne("ALLOW", "Allow", "Allow only while using the app"); PurpleLabeledSwitch purpleLabeledSwitch = new PurpleLabeledSwitch(context); purpleLabeledSwitch.findViewById(R.id.location_switch); purpleLabeledSwitch.setOn(true); assertTrue(purpleLabeledSwitch.isOn()); onView(isRoot()).perform(waitFor(3000)); onView(withId(R.id.location_image_view)) .check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))); onView(withId(R.id.location_image_view)) .check(matches(withDrawable(R.drawable.ic_right_arrow_green))); } // 删掉单独的test5revokePermission方法,没必要了
2. 强制指定测试执行顺序(应急可用,不推荐)
如果你非要保留单独的撤销测试方法,那得强制JUnit按你写的顺序执行。用@FixMethodOrder注解指定按方法名排序:
import org.junit.FixMethodOrder; import org.junit.runners.MethodSorters; @FixMethodOrder(MethodSorters.NAME_ASCENDING) public class YourTestClassName { @Test public void test4clickAllowLocation() { // 测试代码... } @Test public void test5revokePermission() { UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); mDevice.pressHome(); revokePermission(); } public void revokePermission() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); String packageName = instrumentation.getTargetContext().getPackageName(); UiAutomation uiAutomation = instrumentation.getUiAutomation(); uiAutomation.executeShellCommand("pm revoke " + packageName + " android.permission.ACCESS_FINE_LOCATION"); uiAutomation.executeShellCommand("pm revoke " + packageName + " android.permission.ACCESS_COARSE_LOCATION"); } }
NAME_ASCENDING会让JUnit按方法名的字母顺序执行,这样test4肯定在test5前面。但还是要提醒你:这种方式破坏了测试的独立性,一旦test4失败,test5也白搭,所以尽量别用。
3. 优化你的权限撤销方法
顺便提一句,你原来的revokePermission()代码可以简化下,避免重复获取实例,让代码更整洁:
public void revokePermission() { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); String packageName = instrumentation.getTargetContext().getPackageName(); UiAutomation uiAutomation = instrumentation.getUiAutomation(); uiAutomation.executeShellCommand("pm revoke " + packageName + " android.permission.ACCESS_FINE_LOCATION"); uiAutomation.executeShellCommand("pm revoke " + packageName + " android.permission.ACCESS_COARSE_LOCATION"); }
为啥@BeforeClass/@AfterClass能正常工作?
因为这两个注解的方法是和整个测试类绑定的:@BeforeClass在所有测试方法执行前跑一次,@AfterClass在所有测试都跑完后跑一次,它们的生命周期不受单个测试方法的成败影响,所以你在里面执行撤销逻辑不会有问题。但如果你的测试类有多个测试方法,@AfterClass只会清理一次,没法保证每个测试都有干净的环境,还是@After更实用。
内容的提问来源于stack exchange,提问作者mehdi dehghani
相关产品推荐
相关产品推荐

