在 Android 中使用測試替身

測試元素或元素系統時,請個別測試。舉例來說,如果要測試 ViewModel,您不需要啟動模擬器並啟動 UI,因為 ViewModel 不會 (或不應) 依賴 Android 架構。

不過,測試的對象可能依賴其他對象才能運作。舉例來說,ViewModel 可能會依賴資料存放區才能運作。

當您需要為受測試的主體提供依附元件時,常見做法是建立測試替身 (或測試物件)。測試影本是看起來和應用程式中的元件一樣,但會在測試中建立,以提供特定行為或資料的物件。主要優點是可讓測試更快速、更簡單。

測試雙胞胎的類型

測試雙胞胎有以下幾種類型:

測試雙重代理程式,具有類別的「可用」實作項目,但實作方式適合測試,但不適合用於實際工作環境。

範例:記憶體內資料庫。

假資料不需要模擬架構,且體積輕巧。這是首選做法。

模擬 測試雙胞胎會以您程式設計的行為運作,並且對其互動方式有預期。如果模擬資料的互動行為不符合您定義的條件,測試就會失敗。模擬資料通常會使用模擬架構建立,以便達成上述所有目標。

範例:驗證資料庫中的方法是否只呼叫一次。

空白 測試雙胞胎會按照您程式設計的行為運作,但不會對其互動方式有任何期待。通常是使用模擬架構建立。為求簡單,建議使用假資料,而非虛擬資料。
Dummy 傳遞但未使用的測試影本,例如您只需要將其做為參數提供。

範例:傳遞做為點擊回呼的空白函式。

間諜 實體物件的包裝函式,可用於追蹤一些額外資訊,類似模擬物件。通常會避免使用這些函式,以免增加複雜度。因此,假的實作或模擬比間諜更為理想。
Shadow 在 Robolectric 中使用的假資料。

使用假資料的範例

假設您想對 ViewModel 進行單元測試,該 ViewModel 會依賴名為 UserRepository 的介面,並將第一個使用者的名稱公開至 UI。您可以實作介面並傳回已知資料,藉此建立假的測試雙重。

object FakeUserRepository : UserRepository {
    fun getUsers() = listOf(UserAlice, UserBob)
}

val const UserAlice = User("Alice")
val const UserBob = User("Bob")

這個假 UserRepository 不需要依賴實際工作環境版本會使用的本機和遠端資料來源。這個檔案位於測試來源集,不會隨實際工作環境應用程式一併發布。

假依附元件可傳回已知資料,而不需要依附遠端資料來源
圖 1:單元測試中的假依附元件。

以下測試會驗證 ViewModel 是否正確將第一個使用者名稱公開給檢視畫面。

@Test
fun viewModelA_loadsUsers_showsFirstUser() {
    // Given a VM using fake data
    val viewModel = ViewModelA(FakeUserRepository) // Kicks off data load on init

    // Verify that the exposed data is correct
    assertEquals(viewModel.firstUserName, UserAlice.name)
}

在單元測試中,只要是測試人員建立的 ViewModel,就能輕鬆將 UserRepository 替換為假資料。不過,在大型測試中替換任意元素可能相當困難。

取代元件和插入依附元件

如果測試無法控制測試系統的建立作業,則替換測試替身的元件會變得更複雜,而且應用程式的架構必須遵循可測試的設計。

即使是大型端對端測試,也可以透過使用測試替身來發揮效益,例如在應用程式中瀏覽完整使用者流程的設備測試。在這種情況下,您可能需要讓測試密封。密封測試可避免所有外部依附元件,例如從網際網路擷取資料。這有助於提升可靠性和效能。

圖 2:大型測試,涵蓋大部分應用程式並模擬遠端資料。

您可以自行設計應用程式,以便手動實現這種彈性,但我們建議您使用 Hilt依附元件插入架構,在測試期間取代應用程式中的元件。請參閱 Hilt 測試指南

後續步驟

測試策略」頁面說明如何使用不同類型的測試來提高工作效率。