iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 27
0

我們講完了一般的 unit test,是時候來測試一些 Android UI 相關的程式了。
基本上 UI test 會做以下這幾件事情:

  1. 找出某些 View
  2. 操作某些 View
  3. 比對某些 View

我們今天會以 Android 官方推薦的 espresso 為教學,馬上就開始吧!

Install

首先一樣是加上 dependency:

dependencies {
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test:rules:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

還記得我們 /src 下有 /main、/test 跟 /androidTest 三個資料夾嗎?
androidTestImplementation 就是針對 /androidTest 的 dependency 設定。

另外在 android.defaultConfig 必須加上以下設定:

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

以下是一個完整的 build.gradle 範例檔案:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28

    defaultConfig {
        applicationId "com.example.androidtestsample"
        minSdkVersion 15
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
}

dependencies {
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test:rules:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

Usage

我們一開始所說的三件事情,在 espresso 中也有相對應的函示:

  1. 找出某些 View:使用 ViewMatcher
    提供了類似 withIdwithText 等找出 View 的方法。
  2. 操作某些 View:使用 ViewInteraction
    一般先使用 onView 建立 ViewInteraction 然後就可以呼叫 perform 來進行像是 clicktypeTextView 的操作。
  3. 比對某些 View:使用 ViewAssertion
    首先用 matches 建立我們的 ViewAssertion,然後在 ViewInteraction 下使用 check 函式來連結我們的 ViewAssertion

如果大家還是很模糊的話,讓我們來看個例子,假設我們在程式端有以下程式碼:

val account = findViewById<TextView>(R.id.account)
val submit = findViewById<Button>(R.id.submit)
submit.isEnabled = false
account.addTextChangedListener(object : TextWatcher {
    override fun afterTextChanged(s: Editable?) {
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        if (s != null) {
            submit.isEnabled = s.isNotEmpty()
        } else {
            submit.isEnabled = false
        }
    }

})

如果我們想要測試當 account 輸入的時候,submit 的狀態有沒有正常跟著改變就可以這樣寫:

@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
    @Rule
    @JvmField
    val activityRule = ActivityTestRule(LoginActivity::class.java)

    @Test
    fun checkSubmitState() {
        // submit should be disable at first
        onView(withId(R.id.submit))
            .check(matches(not(isEnabled())))

        // type Jintin
        onView(withId(R.id.account))
            .perform(typeText("Jintin"))
        // submit should be enable now
        onView(withId(R.id.submit))
            .check(matches(isEnabled()))

        // clear the text
        onView(withId(R.id.account))
            .perform(clearText())
        // submit should be disable now
        onView(withId(R.id.submit))
            .check(matches(not(isEnabled())))
    }

}

跟 unit test 一樣,不論是函示或 class 前面都有個小綠箭頭可以執行我們的測試。

onView 有點像 espresso 的起手式,裡面的 withId 可以幫我們找到我們關注的 View
接下來藉由 perform 來執行一或多個動作,比如說是 typeTextclickclearText
第三步就是藉由 check 來執行 ViewAssertion 的檢核,matches 會將 ViewMatcher 包裝成 ViewAssertion ,常見的 ViewMatcherisEnabledisDisplayed 等,狀態基本上都是正面表列,有需要的話也可以跟 not 這種可以轉換成反面的 matcher 連起來使用(就像我們的 matches(not(isEnabled())))。

是不是跟測試 3A 很像呢,以上就是今天的內容了,感謝大家!
也歡迎參考 Android 十全大補的測試三部曲的其他文章:

參考資料:
https://developer.android.com/training/testing/espresso


上一篇
[Android 十全大補] Mockito
下一篇
[Android 十全大補] Jenkins
系列文
Android 十全大補30

尚未有邦友留言

立即登入留言