什麼是單元測試

這是一個初級讀本。它主要是因為文件被迫有一個例子,即使它是作為存根文章。如果你已經瞭解了單元測試的基礎知識,請隨時跳到提及特定框架的備註。

單元測試確保給定模組按預期執行。在大規模應用中,確保在真空中適當執行模組是確保應用程式保真度的不可或缺的部分。

考慮以下(普通)偽示例:

public class Example {
  public static void main (String args[]) {
    new Example();
  }

  // Application-level test.
  public Example() {
    Consumer c = new Consumer();
    System.out.println("VALUE = " + c.getVal());
  }

  // Your Module.
  class Consumer {
    private Capitalizer c;
  
    public Consumer() {
      c = new Capitalizer();
    }

    public String getVal() {
      return c.getVal();
    }
  }

  // Another team's module.
  class Capitalizer {
    private DataReader dr;
  
    public Capitalizer() {
      dr = new DataReader();
    }

    public String getVal() {
      return dr.readVal().toUpperCase();
    }
  }

  // Another team's module.
  class DataReader {
    public String readVal() {
      // Refers to a file somewhere in your application deployment, or
      // perhaps retrieved over a deployment-specific network.
      File f; 
      String s = "data";
      // ... Read data from f into s ...
      return s;
    }
  }
}

所以這個例子是微不足道的; DataReader 從檔案中獲取資料,將其傳遞給 CapitalizerCapitalizer 將所有字元轉換為大寫,然後傳遞給 Consumer。但是 DataReader 與我們的應用程式環境密切相關,因此我們推遲測試此鏈,直到我們準備部署測試版本。

現在,假設,在釋出中的某個地方,由於未知的原因,Capitalizer 中的 getVal() 方法從返回 toUpperCase() 字串變為 toLowerCase() 字串:

  // Another team's module.
  class Capitalizer {
    ...

    public String getVal() {
      return dr.readVal().toLowerCase();
    }
  }

顯然,這打破了預期的行為。但是,由於執行 DataReader 涉及的過程繁重,我們在下一次測試部署之前不會注意到這一點。因此,幾天/幾周/幾個月,我們的系統中會出現這個錯誤,然後產品經理會看到這一點,並立即轉向你,即與 Consumer 相關的團隊負責人。 “為什麼會這樣?你們有什麼改變?” 顯然,你是無能為力的。你不知道發生了什麼。你沒有改變任何應該觸及的程式碼; 它為什麼突然壞了?

最終,在團隊和協作之間的討論之後,問題被追蹤,並且問題得到解決。但是,這引出了一個問題; 怎麼可以防止這種情況?

有兩件明顯的事情:

測試需要自動化

我們對手動測試的依賴使得這個 bug 在很長時間內都沒有引起注意。我們需要一種方法來自動化立即引入錯誤的過程。從現在開始不到 5 周。從現在開始不是 5 天。距離現在不到 5 分鐘。馬上。

你必須要明白,在這個例子中,我已經表達了一個引入和忽視的非常微不足道的錯誤。在工業應用中,隨著數十個模組不斷更新,這些模組可以遍佈各處。你用一個模組來修復某些東西,只是意識到你在其他地方(內部或外部)以某種方式依賴你修復的行為。

如果沒有嚴格的驗證,事情就會蔓延到系統中。有可能的是,如果忽略得足夠遠,這將導致如此多的額外工作試圖修復變更(然後修復這些修復等),產品實際上會增加剩餘的工作量。你不想處於這種情況。

測試需要細化

我們上面的例子中提到的第二個問題是跟蹤 bug 所花費的時間。當測試人員發現它時,產品經理給你發了戳,你調查並發現 Capitalizer 正在返回看似不好的資料,你用你的發現,他們調查等等來 ping 了 Capitalizer 團隊等。

我在上面提到的關於這個微不足道的例子的數量和難度的觀點與此相同。顯然,任何理解精通 Java 的人都可以快速找到引入的問題。但追蹤和溝通問題通常要困難得多。也許 Capitalizer 團隊為你提供了沒有來源的 JAR。也許他們位於世界的另一邊,溝通時間非常有限(也許是每天傳送一次的電子郵件)。它可能導致需要數週或更長時間才能跟蹤的錯誤(同樣,對於給定的版本,可能會有幾個錯誤)。

為了減輕這種影響,我們希望在儘可能精細的水平上進行嚴格的測試 (你還需要進行粗粒度測試以確保模組正常互動,但這不是我們的焦點)。我們希望嚴格指定所有外向功能(至少)如何執行,以及對該功能的測試。

進入單元測試

想象一下,如果我們進行了測試,特別是確保 CapitalizergetVal() 方法返回給定輸入字串的大寫字串。此外,假設測試在我們提交任何程式碼之前執行。引入系統的錯誤(即 toUpperCase()toLowerCase() 取代)不會引起任何問題,因為錯誤永遠不會引入系統。我們會在測試中發現它,開發人員(希望)會意識到他們的錯誤,並且會就如何引入他們的預期效果達成替代解決方案。

這裡有一些關於如何實現這些測試的遺漏,但這些遺漏在特定於框架的文件中(在備註中連結)。我們希望,這作為一個例子**,為什麼**單元測試是非常重要的。