Gradle Managed Devicesを使ってみた

本記事は [メグリ株式会社 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/mgre) の記事です。

こんにちは。メグリ株式会社でAndroidエンジニアをしている田邊です。
今回はGradle Managed Devicesを使ってみたという記事を書きたいと思います。

今年10月に開催されたDroidKaigi 2022で外山さんがご発表された「[Gradle Managed Virtual Devicesで変化するエミュレータ活用術](https://speakerdeck.com/sumio/droidkaigi-2022-gradle-managed-virtual-devicesdebian-hua-suruemiyuretahuo-yong-shu)」を聴講し、個人的に試してみたいなと思っていたところ、今回のアドベントカレンダーのネタとしてちょうど良かったので実際に試してみました。

DroidKaigi 2022に参加したときの感想記事も書いていますのでよかったら御覧ください。

https://note.com/saiyou_mgre/n/n4822149218db

### Gradle Managed Devices (GMD) とは

Gradle Managed Devices (GMD) は Android Gradle Plugin 7.2 から導入されたエミュレータを使った自動テストの実行をサポートする機能で、build.gradleにテストに利用するエミュレータ種別を書いておくことで、エミュレータの作成からテストの実行、そしてテストレポート作成までを Gradleが一気に実行してくれるものです。

外山さんの資料には、テストの種類別GMD活用シーンとして「Robolectricが必要なLocal Testの移行」が挙げられていました。
弊社ではRobolectricを利用したテストが多く存在しています。このこと自体が特に問題ということはないのですが、以前にRobolectricのバージョンアップに伴ってテストコードの修正対応が必要だったことから、この対応作業自体を無くしたという要望や、テスト内容によってはエミュレータで実行することでテストの信頼度を上げたいという要望が少なからずあります。

また、GMDでは複数のエミュレータを利用して並行してテストを実行できるため、テスト時間も削減できそうなことから、まずはGMDを試してみるというのが本稿の趣旨です。


### GMDを実行するまでに設定したこと


#### プラグインを更新

まずはGMDが安定して動作するAGPを7.3系以上に設定します。

Android Studioも使ってテストしようと思いますが、Stable版以外を使うとAGPのバージョン判定がシビアとのことなので、標準的な環境としてAndroid Studio Dolphin 2021.3.1 と AGP7.3の組み合わせで試しました。

指定したAGPバージョンは最新版の7.3.1で、これに伴ってGradleバージョンも7.4に更新しています。

```build.gradle
buildscript {
  dependencies {
    classpath "com.android.tools.build:gradle:7.3.1"
  }
}
```

```gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
```


Kotlinのバージョンも更新したところ、以下のエラーとなりました。

```
Execution failed for task ':app:kaptDebugKotlin'.
> A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
> java.lang.reflect.InvocationTargetException (no error message)
```

これはdaggerとmoshiのバージョンが古かったことが原因だったため、それぞれ最新のバージョンに更新しています。


```build.gradle
dependencies {
    implementation "com.google.dagger:dagger-android:2.44.2"
    implementation "com.squareup.moshi:moshi:1.14.0"
}
```

#### build.gradleにエミュレータ種別を記述

https://developer.android.com/studio/test/gradle-managed-devices

[Android Developersのドキュメント](https://developer.android.com/studio/test/gradle-managed-devices)を参考に、以下のように設定しました。
今回はお試しなので１つのエミュレータの記述のみとしています。

```build.gradle
android {
  testOptions {
    managedDevices {
      devices {
        pixel2api30 (com.android.build.api.dsl.ManagedVirtualDevice) {
          device = "Pixel 2"
          apiLevel = 30
          systemImageSource = "aosp"
        }
      }
    }
  }
}
```


#### エミュレータを更新

ここまでで準備ができたので、以下のコマンドでGMDのテストを実行してみます。


```
$ ./gradlew pixel2api30DebugAndroidTest
```

テストは実行されるものの、本来は数分で終了するはずのテストがなかなか終わらず、10分ほど経ってやっと完了しました。レポートを確認したところ、以下のエラーが記載されており、テストが正常には実行されませんでした。


```
Device provider <com.android.tools.utp.plugins.deviceprovider.gradle.GradleManagedAndroidDeviceProvider> failed trying to provide device controller.
Gradle was unable to attach one or more devices to the adb server.
Please ensure that you have sufficient resources to run the requested
number of devices or request fewer devices.
com.android.tools.utp.plugins.deviceprovider.gradle.GradleManagedAndroidDeviceLauncher$EmulatorTimeoutException: Gradle was unable to attach one or more devices to the adb server.
Please ensure that you have sufficient resources to run the requested
number of devices or request fewer devices.
	at com.android.tools.utp.plugins.deviceprovider.gradle.GradleManagedAndroidDeviceLauncher.makeDevice(GradleManagedAndroidDeviceLauncher.kt:133)
	at com.android.tools.utp.plugins.deviceprovider.gradle.GradleManagedAndroidDeviceLauncher.provideDevice(GradleManagedAndroidDeviceLauncher.kt:265)
	at com.android.tools.utp.plugins.deviceprovider.gradle.GradleManagedAndroidDeviceProvider.provideDevice(GradleManagedAndroidDeviceProvider.kt:55)
	at com.google.testing.platform.executor.DeviceProviderProxy$provideDevice$2.invoke(DeviceProviderProxy.kt:118)
	at com.google.testing.platform.executor.DeviceProviderProxy$provideDevice$2.invoke(DeviceProviderProxy.kt:117)
	at com.google.testing.platform.core.telemetry.common.noop.NoopDiagnosticsScope.recordEvent(NoopDiagnosticsScope.kt:35)
	at com.google.testing.platform.core.telemetry.TelemetryKt.recordEvent(Telemetry.kt:66)
	at com.google.testing.platform.executor.DeviceProviderProxy.provideDevice(DeviceProviderProxy.kt:114)
	at com.google.testing.platform.executor.SingleDeviceExecutor$execute$deviceController$1.invoke(SingleDeviceExecutor.kt:68)

...
```

色々試した結果、インストールしているエミュレータのバージョンが古かったのが原因のようです。
Android StudioのSDK ManagerからAndroid Emulatorを最新版に更新してから再度実行したところテストが無事に完了しました。

<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/361241/bf27bad1-c4b0-5a60-0ba1-b3c0cd10e873.jpeg">

これで手元でGMDが使えるところまで設定ができました。

今回はRobolectricを使ったLocalTestもInstrumentedTestに移行して試そうと思いましたが、Mockitoを使ったテストの移行までは実施せず、一部のテストケースでしか動作確認できませんでしたが、それでも思ったよりも実行時間が遅くなっていませんでした。

今後もCIとの連携なども含めて試していきたいと思います。


明日のアドベントカレンダーの記事は @kurashita さんによる 「[ここがつらいよマルチテナント](https://mgre.hateblo.jp/entry/2022/12/15/005550)」 です。