測試氣味-整潔單元測試

PetterLiu發表於2024-07-11

測試氣味Test Smells-整潔單元測試


背景

"Code smell" 是軟體開發中的一個術語,指的是程式碼中可能表明存在問題的某些跡象或模式。這些跡象本身並不表示程式碼一定有錯誤,但它們通常表明程式碼可能難以理解、維護或擴充套件。Code smells 可以視為一種警告,提示開發者需要進一步檢查程式碼以確定是否存在更深層次的問題。

常見的 code smells 包括:

  1. 重複程式碼(Duplication):程式碼中有重複的邏輯或結構。
  2. 過長函式(Long Method):一個函式執行太多工,難以理解和維護。
  3. 過大類(Large Class):類包含太多的屬性和方法,違反了單一職責原則。
  4. 過長引數列表(Long Parameter List):函式或方法有過多的引數,難以使用。
  5. 資料泥團(Data Clumps):多個類或方法中出現相同的資料結構。
  6. 基本型別偏執(Primitive Obsession):過度使用基本資料型別,而不是建立更有意義的類。
  7. 切換/狀態/型別(Switch/State/Type):使用大量的條件語句來處理不同的狀態或型別。
  8. 霰彈式修改(Shotgun Surgery):修改程式碼時需要在多個地方進行更改。
  9. 特徵嫉妒(Feature Envy):函式或方法似乎更關心另一個類的資料而不是自己的。
  10. 資料類(Data Class):類只包含資料和訪問器,沒有行為。

識別和解決 code smells 是重構過程的一部分,可以幫助提高程式碼質量和可維護性。

單元測試中程式碼味道

在實際工作中,知道如何不編寫程式碼可能與知道如何編寫程式碼同樣重要。測試程式碼也是如此;今天,我們將探討編寫單元測試時常見的錯誤。雖然編寫單元測試是程式設計師的常見做法,但測試仍常常被視為二等程式碼。編寫好的測試並不容易--就像在任何程式設計領域一樣,有模式也有反模式。在 Gerard Meszaros 關於 xUnit 模式的書中,有一些關於測試氣味的章節很有幫助,網際網路上也有更多好東西。

一次測試的演變。 首先,我們要測試什麼?一個原始函式:

public String hello(String name) {
return "Hello " + name + "!";
}

我們開始為它編寫單元測試:

@Test
void test() {

}

就這樣,我們的程式碼已經有了味道。

1. 無資訊的名稱

當然,只寫 test、test1、test2 要比寫一個翔實的名稱簡單得多。而且,這樣也更簡短!

但是,寫起來容易的程式碼遠不如讀起來容易的程式碼重要--我們花在閱讀程式碼上的時間更多,而可讀性差會浪費大量時間。名稱應該傳達意圖;應該告訴我們正在測試什麼。

也許我們可以把測試命名為 testHello,因為它測試的是 hello 函式?不行,因為我們不是在測試方法,而是在測試行為。所以好的名字應該是 shouldReturnHelloPhrase:

@Test
void shouldReturnHelloPhrase() {
assert(hello("John")).matches("Hello John!");
}

除了框架之外,沒有人會直接呼叫測試方法,因此名稱太長也沒有關係。它應該是一個描述性的、有意義的短語(DAMP)。


2. 沒有 arrange-act-assert


名字還可以,但現在一行中塞進了太多的程式碼。最好把準備工作、我們要測試的行為和關於該行為的斷言(rangement-act-assert)分開。

image

@Test
void shouldReturnHelloPhrase() {
String a = "John";

String b = hello("John");

assert(b).matches("Hello John!");
}

在 BDD 中,習慣使用 Given-When-Then 模式,在本例中也是如此。


3. 變數名不正確,沒有變數重複使用


但看起來還是寫得很匆忙。a "是什麼?b "是什麼?你可以推斷出一些,但想象一下,這只是測試執行中失敗的幾十個測試中的一個(在幾千個測試的測試套件中完全有可能)。在對測試結果進行排序時,你需要做大量的推斷工作!

因此,我們需要正確的變數名。

我們在匆忙中還做了一件事--我們所有的字串都是硬編碼的。硬編碼某些內容是可以的,但必須與其他硬編碼內容無關!

也就是說,當您閱讀測試時,資料之間的關係應該是顯而易見的。a' 中的 "John "與斷言中的 "John "是否相同?在閱讀或修復測試時,我們不應該在這個問題上浪費時間。

因此,我們可以這樣重寫測試:

@Test
void shouldReturnHelloPhrase() {
String name = "John";

String result = hello(name);
String expectedResult = "Hello " + name + "!";

assert(result).contains(expectedResult);
}


4. 殺蟲劑效應


這裡還有一件事值得思考:自動化測試很好,因為你可以用很少的成本重複測試,但這也意味著隨著時間的推移,它們的有效性會下降,因為你只是在重複測試完全相同的東西。這就是所謂的 "殺蟲劑悖論"(Boris Beizer 在 20 世紀 80 年代創造的術語):蟲子會對你用來殺死它們的東西產生抗藥性。

要完全克服殺蟲劑悖論可能是不可能的,但有一些工具可以透過在測試中引入更多的可變性來減少其影響,例如 Java Faker。讓我們用它來建立一個隨機名稱:

@Test
void shouldReturnHelloPhrase() {
Faker faker = new Faker();
String name = faker.name().firstName();

String result = hello(name);
String expectedResult = "Hello " + name + "!";

assert(result).contains(expectedResult);
}

好在我們在上一步中將名稱改為了變數--現在我們不必再檢視測試,找出所有的 "約翰 "了。


5. 資訊不全的錯誤資訊


如果我們在匆忙中編寫了測試,那麼另一件我們可能沒有考慮到的事情就是錯誤資訊。在對測試結果進行分類時,您需要儘可能多的資料,而錯誤資訊是最重要的資訊來源。然而,預設的錯誤資訊非常缺乏資訊量:

java.lang.AssertionError at org.example.UnitTests.shouldReturnHelloPhrase(UnitTests.java:58)

太好了。我們唯一知道的就是斷言沒有透過。幸好,我們可以使用 JUnit`Assertions` 類中的斷言。具體方法如下

@Test
void shouldReturnHelloPhrase4() {
Faker faker = new Faker();
String name = faker.name().firstName();

String result = hello(name);
String expectedResult = "Hello " + name + "";

Assertions.assertEquals(
result,
expectedResult
);
}

這是新的錯誤資訊:

Expected :Hello Tanja! Actual :Hello Tanja

......這立刻告訴了我們出錯的原因:我們忘記了感嘆號!


經驗教訓

這樣,我們就有了一個很好的單元測試。我們能從這個過程中吸取什麼教訓呢?很多問題都是由於我們有點懶惰造成的。不是那種好的懶惰,你會認真思考如何減少工作量。而是壞的懶惰,即為了 "速戰速決 "而走阻力最小的路。 硬編碼測試資料、剪下和貼上、使用 "test "+方法名稱(或 "test1"、"test2"、"test3")作為測試名稱,這些做法在短期內稍顯簡單,但卻使測試庫更難維護。一方面,我們一直在談論測試的可讀性和易讀性,但同時卻把一行測試變成了 9 行,這有點諷刺。不過,隨著測試數量的增加,我們在此提出的做法將為您節省大量的時間和精力。



今天先到這兒,希望對雲原生,技術領導力, 企業管理,系統架構設計與評估,團隊管理, 專案管理, 產品管理,資訊保安,團隊建設 有參考作用 , 您可能感興趣的文章:
構建創業公司突擊小團隊
國際化環境下系統架構演化
微服務架構設計
影片直播平臺的系統架構演化
微服務與Docker介紹
Docker與CI持續整合/CD
網際網路電商購物車架構演變案例
網際網路業務場景下訊息佇列架構
網際網路高效研發團隊管理演進之一
訊息系統架構設計演進
網際網路電商搜尋架構演化之一
企業資訊化與軟體工程的迷思
企業專案化管理介紹
軟體專案成功之要素
人際溝通風格介紹一
精益IT組織與分享式領導
學習型組織與企業
企業創新文化與等級觀念
組織目標與個人目標
初創公司人才招聘與管理
人才公司環境與企業文化
企業文化、團隊文化與知識共享
高效能的團隊建設
專案管理溝通計劃
構建高效的研發與自動化運維
某大型電商雲平臺實踐
網際網路資料庫架構設計思路
IT基礎架構規劃方案一(網路系統規劃)
餐飲行業解決方案之客戶分析流程
餐飲行業解決方案之採購戰略制定與實施流程
餐飲行業解決方案之業務設計流程
供應鏈需求調研CheckList
企業應用之效能實時度量系統演變

如有想了解更多軟體設計與架構, 系統IT,企業資訊化, 團隊管理 資訊,請關注我的微信訂閱號:

image_thumb2_thumb_thumb_thumb_thumb[2]

作者:Petter Liu
出處:http://www.cnblogs.com/wintersun/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。 該文章也同時釋出在我的獨立部落格中-Petter Liu Blog。

相關文章