在 Android 中使用測試替身

設計元素或系統的測試策略時,有三個相關測試層面:

  • 範圍:測試需要多少程式碼?測試可以驗證單一方法、整個應用程式,或在兩者之間的位置驗證。測試範圍是「測試中」,通常是指「主體測試」(但也稱「系統測試中」或「單元測試」)。
  • 速度:測試的執行速度有多快?測試速度可能介於毫秒到幾分鐘。
  • 擬真度:「真實世界」的測試情況如何?舉例來說,如果測試的程式碼部分需要發出網路要求,測試程式碼實際上是否會發出這個網路要求,還是偽造結果?如果測試實際上能與網路通訊,表示精確度較高。需要權衡的是,測試可能需要較長的執行時間,也可能在網路停止運作或使用成本上產生錯誤。

請參閱測試項目,瞭解如何開始定義測試策略。

隔離與依附元件

測試特定元素或元素系統時,您可以獨立獨立。舉例來說,如要測試 ViewModel,您不需要啟動模擬器並啟動 UI,因為這個架構不 (或不應) 依附 Android 架構。

不過,受測試的主體可能「取決於」其他測試對象。舉例來說,ViewModel 可能需要依附於資料存放區才能運作。

您需要為受測試的主體提供依附元件時,常見的做法是建立「測試替身」 (或「測試物件」)。測試替身是指外觀並做為應用程式中的元件的物件,不過會在測試中建立,用於提供特定行為或資料。主要優點在於能讓您更快速、輕鬆地進行測試。

測試替身類型

測試替身分為多種類型:

具有類別「有效」實作的測試替身,但其實作方式適合測試,但不適合用於實際工作環境。

範例:記憶體內資料庫。

偽造不需模擬架構,而且十分輕巧,這些是建議選項

模擬 這個測試替身,執行程式行為及期望的互動情形。如果模擬的互動不符合您定義的要求,模擬就會失敗。為達成上述所有目標,模擬功能通常是透過模擬架構建立。

範例:確認資料庫中的方法僅呼叫一次。

虛設常式 用來執行程式行為的測試替身,但對其互動情形不如預期。通常以模擬架構建立。為了簡單起見,我們會優先採用虛構資料,而非虛設常式。
假人模特兒 傳遞但未使用的測試替身,例如您只需要提供做為參數即可。

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

Spy 實際物件的包裝函式,也會追蹤部分其他資訊 (類似模擬)。通常要避免增加複雜性。因此,清醒或模擬是比間諜的首選。
陰影 在 Robolectric 中使用的假例項。

使用假資訊的範例

假設您想對 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 替換為假,很容易。然而,在大型測試中取代任意元素並不容易。

取代元件和依附元件插入

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

即使是進行大規模的端對端測試,也能受益於使用測試替身的檢測設備 UI 測試,例如能在應用程式中完整瀏覽整個使用者流程的檢測設備使用者介面測試。在這種情況下,您可能會希望將測試設為「密封」。密封測試可避免所有外部依附元件,例如從網際網路擷取資料。這可以提升可靠性和效能。

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

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

Robolectric

在 Android 上,您可以使用 Robolectric 架構,提供一種特殊的測試替身。Robolectric 可讓您在工作站或持續整合環境中執行測試。搭載一般 JVM,不含模擬器或裝置。這項工具可以使用稱為「陰影」的測試替身,模擬 Android 架構的檢視畫面、資源載入和其他部分的加載作業。

Robolectric 是模擬器,因此不應取代簡單的單元測試,也不應用於進行相容性測試。在某些情況下,這個 API 可以提升速度並降低擬真度,但在某些情況下,則會降低擬真度。UI 測試有一個不錯的方式,是讓測試與 Robolectric 和檢測設備測試相容,並根據測試功能或相容性的需求決定執行這類測試的時機。Espresso 和 Compose 測試都可以在 Robolectric 上執行。