如何为Android仪器化测试授予权限?

36

我有一个可以读取短信的应用程序。当我在调试时,这个应用程序运行良好,但是当我使用安卓仪器化测试进行测试时,它会抛出以下错误:

java.lang.SecurityException: Permission Denial: reading com.android.providers.telephony.SmsProvider

这是我的测试用例

@RunWith(AndroidJUnit4.class)
public class SmsFetcherTest {

   @Test
   public void fetchTenSms() throws Exception {
      // Context of the app under test.
      Context appContext = InstrumentationRegistry.getContext();

      //   Fails anyway.
      //   assertTrue(ContextCompat.checkSelfPermission(appContext,
      //     "android.permission.READ_SMS") == PackageManager.PERMISSION_GRANTED);

      List<Sms> tenSms = new SmsFetcher(appContext)
              .limit(10)
              .get();

      assertEquals(10, tenSms.size());
   }
}

我对仪器测试不太熟悉,这样做是否正确?

或者我有什么遗漏吗?


InstrumentationRegistry.getContext() 不会返回被测试应用的上下文。要获取它,请使用InstrumentationRegistry.getTargetContext()。 - undefined
5个回答

44

使用GrantPermissionRule。具体步骤如下:

将以下依赖项添加到app/build.gradle中:

dependencies {
    ...
    androidTestImplementation 'androidx.test:rules:1.4.0'
}

现在将以下内容添加到您的InstrumentedTest类中:

import androidx.test.rule.GrantPermissionRule;

public class InstrumentedTest {
    @Rule
    public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule.grant(Manifest.permission.READ_SMS);
    ...
}

4
AndroidX链接,依赖项为androidTestImplementation 'androidx.test:rules:1.2.0'。它是一个用于Android测试的库,其中包含了 GrantPermissionRule 规则,可以授予应用程序在运行时需要的权限。 - Stachu
2
对我来说,似乎这个规则并不重要,权限验证总是返回true。 - Akhha8
@Akhha8 如果我想让它返回false,该怎么办? - undefined

10
您可以按照以下方式授予权限:
@RunWith(AndroidJUnit4.class)
public class MyInstrumentationTest {
    @Rule
    public GrantPermissionRule permissionRule = GrantPermissionRule.grant(Manifest.permission.READ_SMS);
    ...

}

找不到GrantPermissionRule类。请提供包名。 - rsanath
请查看此链接:https://developer.android.com/reference/android/support/test/rule/GrantPermissionRule - sanemars
@JavaBanana 在我们的测试类中导入android.support.test.rule.GrantPermissionRule; - Sagar
Manifest.permission中找不到USB设备访问权限的任何内容。有什么建议吗? - 4ntoine
应该是 android.Manifest.permission 而不是 Manifest.permission - parohy

1
如果您在仪器类中使用继承,应该在父类中编写@get:Rule
对于位置:
import androidx.test.rule.GrantPermissionRule

@RunWith(AndroidJUnit4::class)
open class SomeTest {
    @get:Rule
    val permissionRule: GrantPermissionRule = GrantPermissionRule.grant(
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION)

如果您在 Android < Q 的设备上添加了 ACCESS_BACKGROUND_LOCATION 或者在 AndroidManifest 中没有该权限,您将会收到 一个异常


1
为了提高可重用性,我创建了一个接口:
interface WriteExternalPermission {

    @get:Rule val writeExternalPermission: GrantPermissionRule
        get() = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE)

}

你可以将其直接实现在需要该权限的测试类中。

0
有些权限规则不起作用,比如Manifest.permission.BLUETOOTH。不确定为什么会这样。
解决方法是使用Mockito和模拟Context - 这样当系统调用context.checkSelfPermission()时,它会简单地返回Permission Granted。
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {

private Context mockedContext;
private final String[] permissionArrayOne = new String[]{
        Manifest.permission.ACCESS_FINE_LOCATION,
        Manifest.permission.ACCESS_COARSE_LOCATION,
        "android.permission.BLUETOOTH",
        "android.permission.BLUETOOTH_ADMIN",
        "android.permission.BLUETOOTH_SCAN"};

@Before
public void setup(){
    mockedContext = Mockito.mock(Context.class);
    Mockito.when(mockedContext.checkPermission(ArgumentMatchers.anyString(),
            ArgumentMatchers.anyInt(),
            ArgumentMatchers.anyInt())).thenReturn(PackageManager.PERMISSION_GRANTED);
}

 @Test
 public void myPermTest() {
    for (String onePerm : permissionArrayOne) {
        Log.d(TAG, "myPermTest: " + onePerm + " " + mockedContext.checkSelfPermission(onePerm));
        assertEquals(ContextCompat.checkSelfPermission(mockedContext, onePerm), PackageManager.PERMISSION_GRANTED);
    }
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接