iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 10
2
Mobile Development

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

Day10 - Mock Android Framework

我們曾提到當你的程式與Android framework相依,在測試時就會是Instrumented tests,需要在實體裝置或模擬器執行。這會讓你的測試變慢,這篇將介紹怎麼透過Mock Android framework,讓你仍是使用Local unit tests。

延續上一篇註冊的案例。這次我們想要讓使用者在註冊成功時,把註冊的帳號儲存在App裡。在需要的時候,就可以直接使用。
而儲存資料,第一個會想到要用的就是SharePreference了。

我們已經知道寫在Activity的不好測試,所以建立一個Repository來處理儲存帳號至SharedPreference的部分吧

class Repository(val context: Context) {
    //儲存UserId至SharePreference
    fun saveUserId(id: String) {
        val sharedPreference = context.getSharedPreferences("USER_DATA", Context.MODE_PRIVATE)
        sharedPreference.edit().putString("USER_ID", id).commit()
    }
}

在註冊成功時呼叫儲存。

//註冊成功,儲存Id
Repository(this).saveUserId(loginId)

測試repository.saveUserId()

在saveUserId()看到儲存SharedPreference會用到Context,就知道它應該會是一個Instrumented tests。但我們希望可以用Local unit tests就可以在JVM上測試,因為這樣測試的速度較快。

在 Gradle 加上Mockito 框架,我們將使用Mockito來模擬與SharedPreference的互動。還記得我們在介紹假物件Mock時,曾提到這樣的做法。

dependencies {
    testImplementation 'org.mockito:mockito-core:2.21.0'
    testImplementation 'org.mockito:mockito-inline:2.21.0’
}

測試步驟:

  1. Mock Context、SharePreference
  2. 使用when thenReturn 讓Production code 呼叫sharedPreference時回傳模擬的物件
  3. 執行被測試物件:Activity 呼叫repository.saveUserId()
  4. 使用verify method,驗證模擬物件是否有呼叫putString,並傳入正確的參數
  5. 檢查SharedPreference是否有呼叫commit
@Test
fun saveUserId() {
    //步驟1.Mock Context、SharePreference
    val sharedPrefs = mock(SharedPreferences::class.java)
    val sharedPrefsEditor = mock(SharedPreferences.Editor::class.java)
    val context = mock(Context::class.java)

    //步驟2. 使用when thenReturn 讓Production code 呼叫sharedPreference時回傳模擬的物件
    `when`(context.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs)
    `when`(sharedPrefs.edit()).thenReturn(sharedPrefsEditor)
    `when`(sharedPrefsEditor.putString(anyString(), anyString())).thenReturn(sharedPrefsEditor)

    val userId = "A1234567"
    val preKey = "USER_ID"

    //步驟3. 執行被測試物件:Act 呼叫repository.saveUserId()
    val repository = Repository(context)
    repository.saveUserId(userId)

    //步驟4. 使用verify method,驗證模擬物件是否有呼叫putString,並傳入正確的參數。
    verify(sharedPrefsEditor).putString(
        argThat { key -> key == preKey },
        argThat { value -> value == userId }
    )

    //步驟5. 檢查SharedPreference是否有呼叫commit
    verify(sharedPrefsEditor).commit()
}

點綠色三角型,執行測試。

可以看到測試通過,測試的時間是1秒6,這樣的Local tests比起直接用Instrument tests快多了。

https://ithelp.ithome.com.tw/upload/images/20190924/20111896Oyc9NOcHEy.png

練習

直接Mock SharePreference不一定是好的解法。這邊給大家一個練習,另一個做法是增加一個中介層SharePreferenceManager,改去測試有沒有傳送正確的資料到ISharePreferenceManager,而不是去每次都要Mock SharePreference。

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

下一篇我們就來看用搭配Instrument tests的話應該怎麼測試。


上一篇
Day09 - 第一個Android 單元測試
下一篇
Day11 - Instrumented Tests
系列文
Android TDD 測試驅動開發30

尚未有邦友留言

立即登入留言