- Minimum SDK: API 23
- Android Studio 4.0.1
- Kotlin 1.4.0
以前、Androidのテストを、UnitTestにて実施しようとしました。
※AndroidでのUnitTestざっくり入門
ですが、Realmが絡んだテストが実施できず、またRealmの実行を含んだオブジェクトのモック化もうまくいきませんでした。(元の処理が実行されてしまいました。)
なので、方針を変更しInstrumentedTest(androidTest)の方でテストケースを実施するようにします。
やりたいことは次のこととなります。
- 表示の確認
- 入力値の確認
- データベースに格納されているかの確認
- 画面遷移:次のActivityにIntentが渡されているかの確認
準備
Realmの準備はこちらを参照:Android,KotlinでRealm
一番上は、プロジェクトを作成した時に既に入っていました。
dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'
}
テスト対象のアプリ
Viewは省略しますが、次のようなアプリをテスト対象とします。
- textView: 初期表示は
Hello World!
- editText: 入力フォーム
- button1:textViewの内容書き換え
- button2: editTextの内容をデータベースに保存
- button3: 次の画面(SecondActivity)に、editTextの内容を渡して遷移
- SecondActivity: 受け取ったテキストをtextViewに表示します。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
findViewById<Button>(R.id.button1).setOnClickListener {
findViewById<TextView>(R.id.textView).text = "Click"
}
findViewById<Button>(R.id.button2).setOnClickListener {
val text = findViewById<EditText>(R.id.editText).text.toString()
Realm.getDefaultInstance().executeTransaction {
it.copyToRealm(Data(UUID.randomUUID().toString(), text))
}
}
findViewById<Button>(R.id.button3).setOnClickListener {
val text = findViewById<EditText>(R.id.editText).text.toString()
val intent = Intent(this, SecondActivity::class.java)
intent.putExtra("text", text)
startActivity(intent)
}
}
}
open class Data(
@PrimaryKey var id : String = "",
var value: String = ""
) : RealmObject()
テストケース:前処理
テストケースの前処理でRealmをメモリで動かす設定をしておきます。
メモリで動かす設定にしていてもファイルは作られるみたいで、名前を毎回変更しないとエラーになってしまいます。
また、テスト対象のActivityをIntentsTestRuleで定義します。
@RunWith(AndroidJUnit4::class)
class SampleInstTest {
@Before
fun init() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
Realm.init(context)
val builder = RealmConfiguration.Builder()
builder.inMemory()
builder.name(UUID.randomUUID().toString())
Realm.setDefaultConfiguration(builder.build())
}
@get:Rule
val intentRule = IntentsTestRule(MainActivity::class.java)
}
テストケース:表示の確認
初期表示の確認と、ボタンクリック時に表示が変更されているかの確認です。
@Test
fun changeTextView() {
// 初期表示
onView(withId(R.id.textView)).check(matches(withText("Hello World!")))
// ボタン1クリック:表示切り替え
onView(withId(R.id.button1)).perform(ViewActions.click())
onView(withId(R.id.textView)).check(matches(withText("Click")))
}
テストケース:入力値の確認と、データベースの確認
Realmのオブジェクトは、データ登録後に取得しないと反映されていませんでした。
@Test
fun saveData() {
// テキスト入力
onView(withId(R.id.editText)).perform(ViewActions.typeText("InputTest"))
// ボタンク2リック:データベースに保存
onView(withId(R.id.button2)).perform(ViewActions.click())
// 入力したデータが一つ入っていることを確認
var realm = Realm.getDefaultInstance()
Assert.assertEquals(1, realm.where(Data::class.java).count())
val data = realm.where(Data::class.java).findFirst()
Assert.assertEquals("InputTest", data?.value ?: "")
realm.close()
// もう一回クリックで、データが増えていることを確認
onView(withId(R.id.button2)).perform(ViewActions.click())
realm = Realm.getDefaultInstance()
Assert.assertEquals(2, realm.where(Data::class.java).count())
realm.close()
}
テストケース:画面遷移
@Test
fun callAcivity() {
// テキスト入力・ボタン3クリック
onView(withId(R.id.editText)).perform(ViewActions.typeText("NextActivity"))
onView(withId(R.id.button3)).perform(ViewActions.click())
// 遷移先のActivityと、Intentでデータが渡されているかを確認
intended(allOf(
hasComponent(hasShortClassName(".SecondActivity")),
hasExtra("text", "NextActivity"))
)
}
テストケース:遷移先
遷移先の確認は、テスト対象のActivityが変わるため別クラスで行います。
こちらのテスト対象は、ActivityTestRuleで指定します。
@RunWith(AndroidJUnit4::class)
class SecondInstTest {
@get:Rule
val activityRule = ActivityTestRule(SecondActivity::class.java)
@Test
fun startNextActivity() {
// Intentを渡してActivity起動
val intent = Intent()
intent.putExtra("text", "PassedText")
activityRule.launchActivity(intent)
// 表示確認
onView(withId(R.id.textView)).check(matches(withText("PassedText")))
}
}
TestRuleについて
- ActivityTestRule: 基本のルール
- ActivityScenarioRule: Activityの起動、終了など自動でやってくれます。基本はこれを使用すればいいと思います。今回は使ってませんが。
- IntentsTestRule: 画面遷移を伴うテストの時に使用します。
その他
またの機会に、ListViewなどのテストも追記していきます。