Ateam Finergy Inc.× Ateam CommerceTech Inc.× Ateam Wellness Inc. Advent Calendar 2022 の14日目は株式会社エイチームウェルネス @yothio が担当します
レガシーなAndroidアプリ/最新のアーキテクチャを上手く追従できていないAndroidアプリを保守していこうと思った時「ロジックが正しいか」といったテストを書こうとしても、Viewとロジックが分離されていないことがあるためユニットテストを書き進めるのは難しく「見た目上変更がない」「アプリケーション全体で見た時に不具合が気づけるように」という思いの元、E2Eテストから先に作ってみようと思いました
初めに
最近Webばっかり書いていたのもあり「AndroidでのE2Eは難しそう、あんまりイメージがつかない」と漠然に思っていましたが、実際に触ってみると簡単にできたので「難しいと思って手を出したことがない人」に届くと嬉しく思います
「 そもそもE2Eテストってなんだっけ?」といった
E2Eテスト自体については今回は省かせていただきます
Espressoの導入
Androidの公式にも書いてある通りのUIテストライブラリのEspressoを使用してE2Eテストを進めます
基本的には公式ドキュメントに書いてあるとおり進めますが一部非推奨のものなどあるので適宜変更していきます
導入の時にやることとしては
- ライブラリの追加
- テスト実行時のランナーの指定
- テストファイルとコードの作成
ライブラリの追加
Espressoのドキュメントに書かれている通りにtestInstrumentationRunner
の項目追加と下記のライブラリを追加します。
ただ、執筆時のEspressoのドキュメントではDeprecateになっているサンプルコードが存在するので、回避するたandroidx.test.ext:junit-ktx
も追加します。
android {
compileSdk 33
defaultConfig {
applicationId "com.example.e2etest"
minSdk 26
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // <<<----- こちらがなければ追加
}
....
}
dependencies {
.....
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation 'androidx.test:runner:1.4.0'
androidTestImplementation 'androidx.test:rules:1.4.0'
}
テストファイルの追加
プロジェクトの新規作成時にapp/src/androidTest/java/com/~~~
とandroidTestのディレクトリが作成されているはずですがなければ作りましょう
作成したディレクトリにテストファイルを作成します
今回は例として下記の名前で作成しました
app/src/androidTest/java/com/example/e2etest/HelloEspressoTest.kt
テストコードの追加
Espressoのサンプルと基本的に同じですが、ActivityTestRule
がdeprecateとなっているのでactivityScenarioRule
に置き換えています。
こちらが先ほどライブラリ追加であったandroidx.test.ext:junit-ktx
を追加する理由となります。
package com.example.e2etest
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.ext.junit.rules.activityScenarioRule
import androidx.test.filters.LargeTest
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class HelloEspressoTest {
@get:Rule
val activityRule = activityScenarioRule<MainActivity>()
@Test
fun listGoesOverTheFold() {
onView(withText("Hello world!")).check(matches(isDisplayed()))
}
}
activityScenarioRule
については「Espressoのサンプルコード」ではなく
↓の「アプリのアクティビティをテストする」にて使用されているのでこちらを参考する方が良いかも
テストを実行するためには、ActivityもしくはFragmentを起動する必要があります
そのためactivityScenarioRule
を使用してMainActivityを起動します
上記のサンプルコードのように@get:Rule
の対象としてactivityScenarioRule
を指定してますが
アクティビティの起動はテストメソッド内でlaunchActivity
を実行しても同じ結果が得られます
launchActivity使用したテスト
@RunWith(AndroidJUnit4::class)
@LargeTest
class HelloEspressoTest {
@Test
fun listGoesOverTheFold() {
launchActivity<MyActivity>()
onView(withText("Hello world!")).check(matches(isDisplayed()))
}
}
同一のファイル内に複数テストを書く場合はactivityScenarioRule
で書いておくと自動的にアクティビティが立ち上がるので基本的にそちらで良いと思います
テスト書いていく
これまでの手順でテストが動作するようになったので後は好きにE2Eを書いていくだけです
Espressoが提供しているメソッドを利用して、Viewの要素をViewInteraction
として取得し、その要素に対して検証していきます
サンプルコードの例だと
withTextにて「Hello World!」と表示しているViewを取得し
onViewでwithTextの時に取得したViewのViewInteractionを作成し、表示されているかを検証しています
onView(withText("Hello world!")) // Viewの要素を取得
.check(matches(isDisplayed())) // 要素の検証
また、Viewの要素を取得できるためEditText
なら入力
ClickListener
を登録しているならクリックさせて要素を検証してあげると良いでしょう
onView(withId(R.id.button_first))
.perform(click())
注意
さまざまな背景によって、
フラグメントは、特定の親アクティビティまたはフラグメントに依存してはなりません。
スマートフォンアプリに関わるのが久しぶりだったので誤っている箇所やより良い方法などあればコメントや編集リクエストをいただけると助かります
最後に
Android Developersに書かれていますがFragmentはActivityに依存しない設計で組んであれば全体を通してテストをせずにFragment単体でテストすることを推奨していますが
ActivityとFragmentの結合度が高くなっていたりと、上手く分離できていないケースがある場合はActivityを通じてE2Eテストを行い
「テストが通る → リファクタリング → テストが通る → Viewとロジックの分離 → テストが通る → ...
」といったように徐々にシステムを変更していくと安全に改善していけるかと思います
サンプルと作成したコードはこちらのリポジトリに上げてるのでご参考ください
https://github.com/yothio/android-e2e-sample
またEspressoの公式ドキュメント下部にサンプルプロジェクトのリンクがあり、そちらのテストの方が大変参考になるため一度見てみると良いかと思います
https://github.com/android/testing-samples/tree/main/ui/espresso