iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 9
2
Mobile Development

Android TDD 測試驅動開發系列 第 9

Day09 - 第一個Android 單元測試

正式進入Android的單元測試。我們要開始使用Android Studio來開發。新增專案後一樣會看到在Gradle已經加入測試框架JUnit。

測試程式目錄:
app/src/test 這是我們要放單元測試的地方
app/src/AndroidTest 則是放需要Android framework的測試,也就是上一篇提到的Instrumented tests。放在這個目錄的測試只能執行在Android裝置或模擬器。

https://ithelp.ithome.com.tw/upload/images/20190923/20111896DtRiwNM1bb.png

開始第一個Android 單元測試

在寫Android App時,最常寫到的就是Activity了。很容易的你就會把很多的程式都寫在Activity。在Android的寫單元測試要能順利,第一個就是把能不需要寫在Activity的都提出。以下的範例將示範將不需要寫在Activity的提出,為這些邏輯加上測試。

範例-填寫註冊表單

這裡有一個範例是註冊的功能。輸入帳號及密碼後可註冊為會員。

功能描述:
1.帳號至少需6碼,第1碼為英文。
2.密碼至少需8碼,第1碼為英文,並包含1碼數字。
3.點擊「註冊」,若失敗則使用AlertDialg告訴使用者失敗原因。
4.點擊「註冊」,若成功則導至註冊成功頁。

這個範例先不考慮要儲存註冊會員需要呼叫Web API。只是單純的將帳號密碼填好後,做資料檢查,如果符合帳號及密碼的格式,我們就視為成功。

https://ithelp.ithome.com.tw/upload/images/20190923/20111896pKy1tP2HFL.png

在UI畫面(請直接參考附件程式碼)都好了之後,為「註冊」的按鈕寫事件,在Activity的onCreate加入按鈕send的事件,在點下送出button時,檢核帳號是否符合規則。

send.setOnClickListener {

    val loginId = loginId.text.toString()
    val pwd = password.text.toString()

    var isLoginIdOK = false
    //帳號至少6碼,第1碼為英文,
    if (loginId.length >= 8) {
        if (loginId.toUpperCase().first() in 'A'..'Z') {
           isLoginIdOK = true
        }
    }
    ...
}

這裡有一段檢核帳號的邏輯:帳號至少需6碼,第1碼為英文。我想要寫一個測試來檢核邏輯是否正確。但像這樣的寫法,你會發現,寫在Activity就會很難測試。

透過擷取方法讓程式可被測試

這一段檢核帳號密碼欄位規則的邏輯跟UI沒什麼關係,我們可以把他提出到一個類別來檢核帳號的正確性。

將檢核帳號提出方法到RegisterVerifyisLoginIdVerify

class RegisterVerify {

    fun isLoginIdVerify(loginId: String): Boolean {
        var isLoginIdOK = false
        //帳號至少6碼,第1碼為英文,j
        if (loginId.length >= 6) {
            if (loginId.toUpperCase().first() in 'A'..'Z') {
                isLoginIdOK = true
            }
        }
        return isLoginIdOK
    }
}

原Activity,則將檢核方式改為呼叫RegisterVerify().isLoginVerify(loginId)

var isLoginIdOK = RegisterVerify().isLoginIdVerify(loginId)

開始寫測試

將驗證的邏輯提出到RegisterVierify類別後,我們就可以針對它去測試。

在app/src/test裡新增測試

1.新增類別:RegisterVerifyTest
2.新增測試方法:isLoginIdVerify()

https://ithelp.ithome.com.tw/upload/images/20190923/20111896dIULzDb9uu.png

class RegisterVerifyTest {

    @Test
    fun loginVerifyTrue() {
        val registerVerify = RegisterVerify()
        //驗證帳號為A123456,長度滿6個字,驗證結果應為true
        assertTrue(registerVerify.isLoginIdVerify("A123456"))
    }

    @Test
    fun loginVerifyFalse() {
        val registerVerify = RegisterVerify()
        //驗證帳號為A12345,長度不滿6個,驗證結果應為false
        assertFalse(registerVerify.isLoginIdVerify("A1234"))
    }
}

點選綠色三角行,執行測試。結果為綠燈,通過測試。這樣就完成了將與Activity無關的邏輯提出一個類別,來達到可測試的目的了。

https://ithelp.ithome.com.tw/upload/images/20190923/20111896HNC4raIhro.png

像這些用來檢核輸入欄位規則的功能,例如密碼規則、Email、手機號碼規則。為這些檢核撰寫測試,CP值就很高。因為通常這些驗證的正確性都很重要的,也容易寫錯的。但寫起測試卻是很簡單,簡單的擷取方法就可以做到。可不要小看擷取方法,很多時候,就樣做就能讓你的重要程式可以被測試到。

把在Activity裡卻跟View無關的程式,可以提出去的都提出去

當然也不是所有跟View無關的程式碼,都可以用擷取方法處理。更進階的方法,我們就留到第三單元架構篇再介紹怎麼讓View更乾淨。

另外,現實生活你可不能像這個範例,把Production code在沒有測試保護的情況,就直接修改。在沒有單元測試的保護下,如果要重構,你可以先建立一個UI測試來保護你現有的程式碼再進行重構。

範例下載:https://github.com/evanchen76/AndroidUnitTestSample

小技巧
擷取方法的快速鍵 Command + Option + M (Ctrl + Alt + M)


上一篇
Day08 - 在Android 上的測試
下一篇
Day10 - Mock Android Framework
系列文
Android TDD 測試驅動開發30

尚未有邦友留言

立即登入留言