世界級的安卓測試開發流!

在「世界級的安卓測試開發流 — 第一部分」,做者開始了安卓測試開發流的討論。咱們探討了一個軟件工程師開始編寫測試,到發現測試開發中的相關問題的不斷變化。 最後,獲得瞭如下結論:html

  • 測試自動化對於軟件開發的成功是相當重要的android

  • 可測試性代碼對編寫某些特定類型的測試是必須的git

  • 有些開發者在不肯定測試內容和測試方法的狀況下,就開始編寫測試github

  • 測試的質量和可靠度一般達不到咱們的指望性能優化

  • 一個測試開發流對於定義測試內容和方法是必要的服務器

所以,任何應用程序中測試的關鍵部分是:網絡

  • 業務邏輯的測試要獨立於框架或庫app

  • 測試服務器端的API集成框架

  • 從用戶的角度,使用黑盒測試驗收標準性能

在本文中,咱們將探討涉及這幾個部分的幾種測試方案,以確保一個穩固的測試開發流。

業務邏輯的測試要獨立於框架或庫:

首先,檢查業務邏輯是否是真的實現預約的產品需求,是必不可少的。咱們須要將想要測試的代碼隔離出來,而後模擬出不一樣的初始場景,以配置某些組件在運行時的行爲。而後,咱們選擇想要執行的代碼部分進行測試。以後,咱們須要在執行完測試對象後檢查軟件的狀態是否正確。

這個測試方案的關鍵在於依賴反轉原則。經過編寫只依賴於抽象的代碼,咱們就能夠把軟件分離成不一樣的層級。爲了得到依賴的實例,咱們只須要向某個對象發出請求。 或者,一旦實例生成,咱們也能得到之。 咱們的軟件有不少部分須要建立代碼來得到合做者的實例。這時,咱們會使用測試替身來模擬初始場景,或編寫不一樣的行爲程序來設計咱們的測試。經過使用測試替身,咱們能同時模擬產品代碼的行爲和狀態。 同時,它幫助咱們選擇測試的範圍,也即須要測試的代碼量。沒有依賴反轉,全部的類須要獨自獲取本身的依賴。結果會致使類實現和依賴實現相耦合,這樣就沒有辦法借用測試替身來切割產品代碼的執行流程。

一般,在構造時傳遞類依賴是使用依賴反轉的最有效機制。這個機制對採用測試替身已經足夠。在構造時傳遞類依賴將幫助咱們建立相應的測試替身來替換依賴的實例。要謹記,使用服務定位器或依賴反轉框架將有助於減小在應用依賴反轉時所須要的樣板, 雖然這並非強制的。

咱們將用一個具體例子(該測試和筆者幾個月前開始開發的 Android GameBoy 模擬器有關)來演示如何測試咱們的業務需求。

如下測試是關於 GameBoy 內存管理單元(MMU)和 GameBoy BIOS 執行單元的。 咱們將檢查產品需求(硬件模擬)是否正確實現。

public class MMUTest {  
  private static final int MMU_SIZE = 65536;
  private static final int ANY_ADDRESS = 11;
  private static final byte ANY_BYTE_VALUE = 0x11;

  @Test public void shouldInitializeMMUFullOfZeros() {
    MMU mmu = givenAMMU();

    assertMMUIsFullOfZeros(mmu);
  }

  @Test public void shouldFillMMUWithZerosOnReset() {
    MMU mmu = givenAMMU();

    mmu.writeByte(ANY_ADDRESS, ANY_BYTE_VALUE);
    mmu.reset();

    assertMMUIsFullOfZeros(mmu);   
  }

  @Test public void shouldWriteBigBytesValuesAndRecoverThemAsOneWord() {
    MMU mmu = givenAMMU();

    mmu.writeByte(ANY_ADDRESS, (byte) 0xFA);
    mmu.writeByte(ANY_ADDRESS +1, (byte) 0xFB);

    assertEquals(0xFBFA, mmu.readWord(ANY_ADDRESS));
  }
}

前三個測試是檢查 GameBoy 的 MMU 實現是否正確。 成功的關鍵是總在測試執行完成後,檢查 MMU 的狀態是否正確。 全部測試都會檢查 MMU 是否正確初始化。若是重置後MMU 是整潔的,寫入2個字節後讀出的是一個字符,那麼最終讀取就是正確的。爲了測試模擬器軟件的這個部分,咱們將測試範圍縮小爲一個類。

public class GameBoyBIOSExecutionTest {

  @Test 
  public void shouldIndicateTheBIOSHasBeenLoadedUnlockingTheRomMapping() {
    GameBoy gameBoy = givenAGameBoy();

    tickUntilBIOSLoaded(gameBoy);

    assertEquals(1, mmu.readByte(UNLOCK_ROM_ADDRESS) & 0xFF);
  }

  @Test
  public void shouldPutTheNintendoLogoIntoMemoryDuringTheBIOSThirdStage() {
    GameBoy gameBoy = givenAGameBoy();

    tickUntilThirdStageFinished(gameBoy);

    assertNintendoLogoIsInVRAM();
  }

  private GameBoy givenAGameBoy() {
    z80 = new GBZ80();
    mmu = new MMU();
    gpu = new GPU(mmu);
    GameLoader gameLoader = new GameLoader(new FakeGameReader());
    GameBoy gameBoy = new Gameboy(z80, mmu, gpu, gameLoader);
    return gameboy;
  }
  
}

在這兩個測試中,咱們檢查 BIOS 是否在不一樣階段都正確執行。BIOS執行結束後,在具體內存位置的一個字節必須被初始化爲一個具體的值。而後,在第三階段的最後,任天堂的logo必須已經加載到 VRAM。因爲全套的BIOS執行是任何模擬器開發的關鍵部分,咱們決定採用更大的測試範圍。這個測試用例的測試對象是 CPU,部分 CPU 指令集(僅涉及 BIOS執行的指令)和 MMU。 要檢查執行的狀態是否正確,咱們必須對 MMU的狀態使用斷言(assert)。要想顯著的提高測試質量,一個關鍵就是檢查軟件執行後的最終狀態,同時避免驗證和其餘組件之間的交互。由於即便和其餘組件之間的交互都是正確的,最終狀態依然多是錯的。還要明確,這些測試的某些部分一樣能夠獨立進行,如 CPU指令。

這些測試的另外一個主要亮點是使用測試替身來模擬和 Android SDK 相關的部分代碼。在執行 BIOS 前,GameBoy 遊戲必須加載進 GameBoy 的 MMU。然而,在測試期間,沒有 Android SDK 可用,於是就不得不將其替換,轉而從測試環境中加載 GameBoy的 rom。咱們使用依賴反轉原則不只用於隱藏實現細節,或者定義邊界,還用測試替身 FakeGameReader 來替代 AndroidGameReader 的產品代碼,這意味着徹底不依賴框架和庫進行代碼測試。經過這樣作,咱們建立了隔離的測試環境,同時調整了測試範圍。

範圍:

調整測試的範圍是很是重要的。 開始編寫測試前,咱們應當牢記測試範圍能夠幫助識別代碼錯誤(取決於測試的規模)。縮小測試規模能帶給咱們更豐富的錯誤反饋,而放大規模的測試則不能提供錯誤位置的精確信息。測試的粒度則應當和測試範圍至關。

基礎設施:

編寫這些測試的基礎設施至關明瞭。 咱們必須在依賴反轉原則下編寫可測試代碼,並結合使用一個測試框架和一個模擬庫。 模擬庫用來生成模擬場景中須要的測試替身,或者用於替代部分產品代碼的測試替身。請注意,這些框架和庫不是強制使用的,可是很是推薦。

結果:

這個方案的結果頗有趣。 當遵循依賴反轉原則時,咱們能獨立於框架和庫測試咱們產品代碼中的業務邏輯。經過可重複的,易於編寫和設計的測試,咱們能夠建立隔離的測試環境。此外,咱們可以方便選擇要測試的產品代碼數量,並使用測試替身代替這部分代碼,來模擬行爲和不一樣的場景。

一旦咱們可以測試產品需求是否正確實現,咱們必須繼續測試開發流。接下來要測試的,就是在前一階段使用測試替身替換的外部組件集成後是否執行正確。咱們將在下一篇博文中討論,敬請期待!

原文地址:http://blog.karumi.com/world-class-testing-development-pipeline-for-android-part-2/

OneAPM Mobile Insight ,監控網絡請求及網絡錯誤,提高用戶留存。訪問 OneAPM 官方網站感覺更多應用性能優化體驗,想閱讀更多技術文章,請訪問 OneAPM 官方技術博客
本文轉自 OneAPM 官方博客

相關文章
相關標籤/搜索