9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Robolectricで GooglePlayServiceいりアプリをテストする

Last updated at Posted at 2016-01-29

Google Play Serviceが入ったアプリを Robolectric (正確には Robospock) でテストしようとしたらハマったのでメモ。

追加: 偉そうに長々書きましたが、正しくは RobolectricGradleTestRunnerGradleRoboSputnik を使うのがいいのかもしれません。でも一々 @RunWith で指定するのは面倒そうな気もする。

問題1:正しい AndroidManifestを見つけてくれない

GooglePlayServiceを使うには AndroidManifestに metaタグで versionを仕込まなくてはいけません。でも、正しい AndroidManifestを見てくれないと、それが定義されてないのでテストの初期化で以下のように言って落ちます:

java.lang.RuntimeException: java.lang.IllegalStateException: A required meta-data tag in your app's AndroidManifest.xml does not exist.  You must have the following declaration within the <application> element:

問題2: @integer/google_play_services_version を見つけてくれない

単純に @Config とかで src/main/AndroidManifest.xml を指定すると今度は次のようなエラーが出ます。

Caused by: java.lang.NullPointerException
	at org.robolectric.manifest.MetaData.init(MetaData.java:55)
	at org.robolectric.manifest.AndroidManifest.initMetaData(AndroidManifest.java:343)

これは、google_play_services_versionsrc/main/res 以下ではなく、Google Play service の aarで定義されてるためです。そのため、参照を解決できなくて NPEになるようです。

解法: Gradleが処理してくれた manifest, resourceを使う

これらの問題を解決するには実際の Android端末で起こることの真似をして、Robolectricが APKに含まれるのと同じファイルを参照するようにすればいいのです。具体的には manifest, resource, assetのディレクトリを正しく指定してやればいいのです。

また、こうすればそれ以外の問題 (flavorごとにレイアウトの違いもテストしたいとか) も解消できて一挙両得です。

しかしそれらのファイルが生成されるディレクトリは buildTypeや flavorで違うので @Configでソース中に指定するわけにも行きません。というわけで、お馴染みの gradle hackで乗り切ります。

testOptions {
        unitTests {
            all {
                // Google Play Service requires meta tag in manifest
                // and merged resource referred by it.
                def words = it.name.split("(?=[A-Z])")*.toLowerCase()
                def variantDir = "${words[1]}/${words[2]}"
                def dir='build/intermediates'
                def manifest="${dir}/manifests/full/${variantDir}/AndroidManifest.xml"
                def resourceDir="${dir}/res/merged/${variantDir}"
                def assetDir="${dir}/assets/${variantDir}"
                jvmArgs "-Dandroid.manifest=${manifest}"
                jvmArgs "-Dandroid.resources=${resourceDir}"
                jvmArgs "-Dandroid.assets=${assetDir}"
            }
        }
    }

これで無事ただしい Manifestやらを読んでくれるようになりました。
ちなみに、build variantごとに applicationIdを変えてる場合、やや面倒なことになるというのを聞きましたが、よくわかっておりません。

注意書き

ちなみに自分は Robospockで試しました。多分素の Robolectricでも使えると思いますが、試してませんので各自注意してお使いください。

また、書いていて気が付きましたが、上の例は flavor と buildTypeを組み合わせている例です。flavorを使っていないとちょこっと書き換えないと行けないとは思います。すみません。

9
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?