私はテストにエスプレッソを使用していますが、外部ストレージからイメージを取得しようとすることがあり、Marshmallowではランタイムのアクセス許可が必要です。そうしないと、例外がクラッシュし、テストが失敗します。
androidTestCompile 'com.Android.support.test:runner:0.4'
androidTestCompile 'com.Android.support.test:rules:0.4'
androidTestCompile 'com.Android.support.test.espresso:espresso-core:2.2.1'
androidTestCompile 'com.Android.support.test.espresso:espresso-intents:2.2.1'
androidTestCompile('com.Android.support.test.espresso:espresso-contrib:2.2.1') {
// this library uses the newest app compat v22 but the espresso contrib still v21.
// you have to specifically exclude the older versions of the contrib library or
// there will be some conflicts
exclude group: 'com.Android.support', module: 'appcompat'
exclude group: 'com.Android.support', module: 'support-v4'
exclude module: 'recyclerview-v7'
}
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'com.squareup.retrofit:retrofit-mock:1.9.0'
androidTestCompile 'com.squareup.assertj:assertj-Android:1.1.0'
androidTestCompile 'com.squareup.spoon:spoon-client:1.2.0'
どうすれば適切に管理できますか?
ランタイムパーミッションのテストを作成する必要がありますか、それともテスト用に無効にする方法はありますか?
彼女がここで言うように、テストを実行する前に許可を与えるべきですか? https://www.youtube.com/watch?list=PLWz5rJ2EKKc-lJo_RGGXL2Psr8vVCTWjM&v=C8lUdPVSzDk
Android gradleタスクを作成して権限を付与できます:
Android.applicationVariants.all { variant ->
def applicationId = variant.applicationId
def adb = Android.getAdbExe().toString()
def variantName = variant.name.capitalize()
def grantPermissionTask = tasks.create("grant${variantName}Permissions") << {
"${adb} devices".execute().text.eachLine {
if (it.endsWith("device")){
def device = it.split()[0]
println "Granting permissions on devices ${device}"
"${adb} -s ${device} Shell pm grant ${applicationId} Android.permission.CAMERA".execute()
"${adb} -s ${device} Shell pm grant ${applicationId} Android.permission.ACCESS_FINE_LOCATION".execute()
}
}
}
}
そして、これはタスクを実行するコマンドです:gradle grantDebugPermissions
UPDATE!使用できるようになりました Rule from Android Testing Support Library
カスタムルールよりも使用する方が適切です。
古い回答:
テストルールを追加してコードを再利用し、柔軟性を高めることができます。
/**
* This rule adds selected permissions to test app
*/
public class PermissionsRule implements TestRule {
private final String[] permissions;
public PermissionsRule(String[] permissions) {
this.permissions = permissions;
}
@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
allowPermissions();
base.evaluate();
revokePermissions();
}
};
}
private void allowPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
for (String permission : permissions) {
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
"pm grant " + InstrumentationRegistry.getTargetContext().getPackageName()
+ " " + permission);
}
}
}
private void revokePermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
for (String permission : permissions) {
InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
"pm revoke " + InstrumentationRegistry.getTargetContext().getPackageName()
+ " " + permission);
}
}
}
}
その後、テストクラスでこのルールを使用できます。
@Rule
public final PermissionsRule permissionsRule = new PermissionsRule(
new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS});
心に留めておいてください:
次を使用して権限を付与および取り消すことができます。
adb Shell pm grant com.package.myapp Android.permission.<PERMISSION>
adb Shell pm revoke com.package.myapp Android.permission.<PERMISSION>
From Javaインストルメンテーションテストを使用するには、Googleサンプルからこのメソッドを呼び出します。 https://github.com/googlesamples/Android-testing/blob/ed62c450e43f859333b3113d44dd59f75971b529/ui/espresso/IntentsBasicSample/ app/src/androidTest/Java/com/example/Android/testing/espresso/BasicSample/DialerActivityTest.Java#L94
GrantPermissionRule を使用できます。このルールは、そのテストクラスのすべてのテストメソッドに対して、要求されたすべてのランタイムアクセス許可を付与します。
@Rule
public GrantPermissionRule mRuntimePermissionRule
= GrantPermissionRule.grant(Manifest.permission.READ_PHONE_STATE);
これは、テストを開始する前に許可を与えることで簡単に実現できます。たとえば、テスト実行中にカメラを使用することになっている場合、次のように許可を与えることができます
@Before
public void grantPhonePermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getInstrumentation().getUiAutomation().executeShellCommand(
"pm grant " + getTargetContext().getPackageName()
+ " Android.permission.CAMERA");
}
}
Android Testing Support LibraryにGrantPermissionRuleがあります。これをテストで使用して、テストを開始する前に許可を与えることができます。
@Rule public GrantPermissionRule permissionRule = GrantPermissionRule.grant(Android.Manifest.permission.CAMERA);
マルチフレーバーのセットアップでは、計測タスクが何であれ、たとえばconnectedYourFlavorDebugAndroidTest
である場合、接続されたすべてのデバイスでテストを実行する前に付与したいアクセス許可を指定できます。
gradlew grantYourFlavorDebugPermissions -Ppermissions=Android.permission.ACCESS_FINE_LOCATION,Android.permission.ACCESS_COARSE_LOCATION
sfjavaのスニペット を参照してbuild.gradle
はgrantYourFlavorDebugPermissions
タスクを生成します
上記のスニペット(riwnodennykの小道具)へのわずかなマイナーアップデート-これは、SDK 24に対してビルドし、ツールバージョン24.0.0を使用して構築する際に非常に役立ちました。
import com.Android.ddmlib.AndroidDebugBridge
import com.Android.ddmlib.IShellOutputReceiver
import com.Android.ddmlib.IDevice
import Java.util.concurrent.TimeUnit
Android.applicationVariants.all { variant ->
def applicationId = [variant.mergedFlavor.applicationId, variant.buildType.applicationIdSuffix].findAll().join()
def grantPermissionsTask = tasks.create("grant${variant.name.capitalize()}Permissions") << {
if (!project.hasProperty('permissions')) {
throw new GradleException("Please add the comma-separated command line parameter, for example -Ppermissions=Android.permission.WRITE_EXTERNAL_STORAGE")
}
AndroidDebugBridge adb = initAdb(Android.getAdbExe().toString())
grantPermissionsOnAllConnectedDevice(adb, applicationId, project.properties['permissions'].split(','))
}
grantPermissionsTask.description = "Grants permissions for ${variant.name.capitalize()}."
grantPermissionsTask.dependsOn "install${variant.name.capitalize()}"
}
public static Object grantPermissionsOnAllConnectedDevice(AndroidDebugBridge adb, String applicationId, String[] permissionNames) {
return adb.getDevices().each {
device ->
int apiLevel = Integer.parseInt(device.getProperty(IDevice.PROP_BUILD_API_LEVEL))
if (0 < apiLevel && apiLevel < 23) {
println "\nSkipping granting permissions for " + device.serialNumber + " because has API level " + device.apiLevel + " < 23"
return
}
println "\nGranting permissions for " + applicationId + " on " + device.serialNumber
permissionNames.each {
permissionName ->
def shellGrantCommand = "pm grant " + applicationId + " " + permissionName
println(shellGrantCommand)
device.executeShellCommand(shellGrantCommand, new IShellOutputReceiver() {
@Override
void addOutput(byte[] data, int offset, int length) {
println new String(data[offset..(offset + length - 1)] as byte[])
}
@Override
void flush() {
}
@Override
boolean isCancelled() {
return false
}
})
}
}
}
public static AndroidDebugBridge initAdb(String path) {
AndroidDebugBridge.initIfNeeded(false)
AndroidDebugBridge adb = AndroidDebugBridge.createBridge(path, false)
waitForAdb(adb, 15000)
return adb
}
private static void waitForAdb(AndroidDebugBridge adb, long timeOutMs) {
long sleepTimeMs = TimeUnit.SECONDS.toMillis(1);
while (!adb.hasInitialDeviceList() && timeOutMs > 0) {
try {
Thread.sleep(sleepTimeMs);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
timeOutMs -= sleepTimeMs;
}
if (timeOutMs <= 0 && !adb.hasInitialDeviceList()) {
throw new RuntimeException("Timeout getting device list.", null);
}
}
ラッパークラスを活用し、バリアント構成をオーバーライドしてビルドするソリューションを実装しました。ソリューションは説明するのに非常に長く、ここにあります: https://github.com/ahasbini/AndroidTestMockPermissionUtils 。スクリプトをビルドシステムに追加したり、テストを実行する前に実行したりする必要はありません。
SDKにはまだパックされていませんが、主なアイデアは、ContextWrapper.checkSelfPermission()
およびActivityCompat.requestPermissions()
の機能をオーバーライドして、テスト対象のさまざまなシナリオにアプリをtrickす模擬結果を返すことです。のような:許可が拒否されたため、アプリはそれを要求し、許可が付与されて終了しました。このシナリオは、アプリにずっと許可があったとしても発生しますが、そのアイデアは、オーバーライドする実装からの模擬結果によってresultsされたということです。
さらに、この実装にはTestRule
クラスと呼ばれるPermissionRule
クラスがあり、これをテストクラスで使用して、すべての条件を簡単にシミュレートして、アクセス許可をシームレスにテストできます。また、たとえばアプリがrequestPermissions()
を呼び出したことを確認するようにアサーションを作成することもできます。
Espressoに最新の「com.Android.support.test.espresso:espresso-core:3.0.1」ライブラリを使用している場合、これは1行のコードで実行できます。必要なのは、テストクラスにルールを追加するだけで、機能を付与するための機能パラメーターとして必要な権限を追加し続けるだけです。下記参照:
@Rule
public GrantPermissionRule mRuntimePermissionRule = GrantPermissionRule .grant(Manifest.permission.READ_PHONE_STATE, Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.BLUETOOTH,Manifest.permission.RECORD_AUDIO);
https://developer.Android.com/reference/Android/support/test/rule/GrantPermissionRule.html
Android.support.test.uiautomator.UiDevice mDevice;
@Before
public void setUp() throws Exception {
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
}
@Test
public void testMainActivityScreenshots() {
allowPermissionsIfNeeded();//allowPermissions on Activity
}
private void allowPermissionsIfNeeded() {
if (Build.VERSION.SDK_INT >= 23) {
UiObject allowPermissions = mDevice.findObject(
new UiSelector().className("Android.widget.Button")
.resourceId("com.Android.packageinstaller:id/permission_allow_button"));// get allow_button Button by id , because on another device languages it is not "Allow"
if (allowPermissions.exists()) {
try {
allowPermissions.click();
allowPermissionsIfNeeded();//allow second Permission
} catch (UiObjectNotFoundException e) {
Timber.e(e, "There is no permissions dialog to interact with ");
}
}
}
}