天道不一定酬所有勤
但是,天道只酬勤

天津11选5开奖结果查询:單元測試的基本準則

開發十年,就只剩下這套架構體系了??!

天津11选5蛋托玩法 www.ijudhr.com.cn 實施單元測試的時候, 如果沒有一份經過實踐證明的詳細規范, 很難掌握測試的 “度”, 范圍太小施展不開, 太大又侵犯 “別人的” 地盤. 上帝的歸上帝, 凱撒的歸凱撒, 給單元測試念念緊箍咒不見得是件壞事, 反而更有利于發揮單元測試的威力, 為代碼重構和提高代碼質量提供動力.

這份文檔來自 Geotechnical, 是一份非常難得的經驗準則. 你完全可以以這份準則作為模板, 結合所在團隊的經驗, 整理出一份內部單元測試準則.

1. 保持單元測試小巧, 快速

理論上, 任何代碼提交前都應該完整跑一遍所有測試套件. 保持測試代碼執行快能夠縮短迭代開發周期.

2. 單元測試應該是全自動/非交互式的

測試套件通常是定期執行的, 執行過程必須完全自動化才有意義. 輸出結果需要人工檢查的測試不是一個好的單元測試.

3. 讓單元測試很容易跑起來

對開發環境進行配置, 最好是敲一條命令或是點擊一個按鈕就能把單個測試用例或測試套件跑起來.

4. 對測試進行評估

對執行的測試進行覆蓋率分析, 得到精確的代碼執行覆蓋率, 并調查哪些代碼未被執行.

5. 立即修正失敗的測試

每個開發人員在提交前都應該保證新的測試用例執行成功, 當有代碼提交時, 現有測試用例也都能跑通.

如果一個定期執行的測試用例執行失敗, 整個團隊應該放下手上的工作先解決這個問題.

6. 把測試維持在單元級別

單元測試即類 (Class) 的測試. 一個 “測試類” 應該只對應于一個 “被測類”, 并且 “被測類” 的行為應該被隔離測試. 必須謹慎避免使用單元測試框架來測試整個程序的工作流, 這樣的測試即低效又難維護. 工作流測試 (譯注: 指跨???類的數據流測試) 有它自己的地盤, 但它絕不是單元測試, 必須單獨建立和執行.

7. 由簡入繁

再簡單的測試也遠遠勝過完全沒有測試. 一個簡單的 “測試類” 會促使建立 “被測類” 基本的測試骨架, 可以對構建環境, 單元測試環境, 執行環境以及覆蓋率分析工具等有效性進行檢查, 同時也可以證明 “被測類” 能夠被整合和調用.

下面便是單元測試版的 Hello, world! :

void testDefaultConstruction()
{
Foo foo = new Foo();
assertNotNull(foo);
}

8. 保持測試的獨立性

為了保證測試穩定可靠且便于維護, 測試用例之間決不能有相互依賴, 也不能依賴執行的先后次序.

9. Keep tests close to the class being tested

[譯注: 有意翻譯該規則, 個人認為本條規則值得商榷, 大部分 C++, Objective-C和 Python 庫均把測試代碼從功能代碼目錄中獨立出來, 通常是創建一個和 src 目錄同級的 tests 目錄, 被測???類名之前也常常 不加 Test 前綴. 這么做保證功能代碼和測試代碼隔離, 目錄結構清晰, 并且發布源碼的時候更容易排除測試用例.]

If the class to test is Foo the test class should be called FooTest (not TestFoo) and kept in the same package (directory) as Foo. Keeping test classes in separate directory trees makes them harder to access and maintain.

Make sure the build environment is configured so that the test classes doesn't make its way into production libraries or executables.

10. 合理的命名測試用例

確保每個方法只測試 “被測類” 的一個明確特性, 并相應的命名測試方法. 典型的命名俗定是test[what], 比如 testSaveAs(), testAddListener(), testDeleteProperty() 等.

11. 只測公有接口

單元測試可以被定義為 通過類的公有 API 對類進行測試. 一些測試工具允許測試一個類的私有成員, 但這種做法應該避免, 它讓測試變得繁瑣而且更難維護. 如果有私有成員確實需要進行直接測試, 可以考慮把它重構到工具類的公有方法中. 但要注意這么做是為了改善設計, 而不是幫助測試.

12. 看成是黑盒

站在第三方使用者的角度, 測試一個類是否滿足規定的需求. 并設法讓它出問題.

13. 看成是白盒

畢竟被測試類是程序員自寫自測的, 應該在最復雜的邏輯部分多花些精力測試.

14. 芝麻函數也要測試

通常建議所有重要的函數都應該被測試到, 一些芝麻方法比如簡單的 settergetter 都可以忽略. 但是仍然有充分的理由支持測試芝麻函數:

芝麻 很難定義. 對于不同的人有不同的理解.

從黑盒測試的觀點看, 是無法知道哪些代碼是芝麻級別的.

即便是再芝麻的函數, 也可能包含錯誤, 通常是 “復制粘貼” 代碼的后果:

private double weight_;
private double x_, y_;

public void setWeight(int weight)
{
  weight = weight_;  // error
}

public double getX()
{
  return x_;
}

public double getY()
{
  return x_;  // error
}

因此建議測試所有方法. 畢竟芝麻用例也容易測試.

15. 先關注執行覆蓋率

區別對待 執行覆蓋率 和 實際測試覆蓋率. 測試的最初目標應該是確保較高的執行覆蓋率. 這樣能保證代碼在 少量 參數值輸入時能執行成功. 一旦執行覆蓋率就緒, 就應該開始改進測試覆蓋率了. 注意, 實際的測試覆蓋率很難衡量 (而且往往趨近于 0%).

思考以下公有方法:

void setLength(double length);

調用 setLength(1.0)你可能會得到 100% 的執行覆蓋率. 但要達到 100% 的實際測試覆蓋率, 有多少個 double 浮點數這個方法就必須被調用多少次, 并且要一一驗證行為的正確性. 這無疑是不可能的任務.

16. 覆蓋邊界值

確保參數邊界值均被覆蓋. 對于數字, 測試負數, 0, 正數, 最小值, 最大值, NaN (非數字), 無窮大等. 對于字符串, 測試空字符串, 單字符, 非 ASCII 字符串, 多字節字符串等. 對于集合類型, 測試空, 1, 第一個, 最后一個等. 對于日期, 測試 1月1號, 2月29號, 12月31號等. 被測試的類本身也會暗示一些特定情況下的邊界值. 要點是盡可能徹底的測試這些邊界值, 因為它們都是主要 “疑犯”.

17. 提供一個隨機值生成器

當邊界值都覆蓋了, 另一個能進一步改善測試覆蓋率的簡單方法就是生成隨機參數, 這樣每次執行測試都會有不同的輸入.

想要做到這點, 需要提供一個用來生成基本類型 (如: 浮點數, 整型, 字符串, 日期等) 隨機值的工具類. 生成器應該覆蓋各種類型的所有取值范圍.

如果測試時間比較短, 可以考慮再裹上一層循環, 覆蓋盡可能多的輸入組合. 下面的例子是驗證兩次轉換 little endianbig endian 字節序后是否返回原值. 由于測試過程很快, 可以讓它跑上個一百萬次.

void testByteSwapper()
{
  for (int i = 0; i < 1000000; i++) {
    double v0 = Random.getDouble();
    double v1 = ByteSwapper.swap(v0);
    double v2 = ByteSwapper.swap(v1);
    assertEquals(v0, v2);
  }
}

18. 每個特性只測一次

在測試模式下, 有時會情不自禁的濫用斷言. 這種做法會導致維護更困難, 需要極力避免. 僅對測試方法名指示的特性進行明確測試.

因為對于一般性代碼而言, 保證測試代碼盡可能少是一個重要目標.

19. 使用顯式斷言

應該總是優先使用 assertEquals(a, b) 而不是 assertTrue(a == b), 因為前者會給出更有意義的測試失敗信息. 在事先不確定輸入值的情況下, 這條規則尤為重要, 比如之前使用隨機參數值組合的例子.

20. 提供反向測試

反向測試是指刻意編寫問題代碼, 來驗證魯棒性和能否正確的處理錯誤.

假設如下方法的參數如果傳進去的是負數, 會立馬拋出異常:

void setLength(double length) throws IllegalArgumentException

可以用下面的方法來測試這個特例是否被正確處理:

try {
  setLength(-1.0);
  fail();  // If we get here, something went wrong
}
catch (IllegalArgumentException exception) {
  // If we get here, all is fine
}

21. 代碼設計時謹記測試

編寫和維護單元測試的代價是很高的, 減少代碼中的公有接口和循環復雜度是降低成本, 使高覆蓋率測試代碼更易于編寫和維護的有效方法.

一些建議:

使類成員常量化, 在構造函數中進行初始化. 減少 setter 方法的數量.

限制過度使用繼承和公有虛函數.

通過使用友元類 (C++) 或包作用域 (Java) 來減少公有接口.

避免不必要的邏輯分支.

在邏輯分支中編寫盡可能少的代碼.

在公有和私有接口中盡量多用異常和斷言驗證參數參數的有效性.

限制使用快捷函數. 對于黑箱而言, 所有方法都必須一視同仁的進行測試. 考慮以下簡短的例子:

public void scale(double x0, double y0, double scaleFactor)
{
  // scaling logic
}

public void scale(double x0, double y0)
{
  scale(x0, y0, 1.0);
}

刪除后者可以簡化測試, 但用戶代碼的工作量也將略微增加.

22. 不要訪問預設的外部資源

單元測試代碼不應該假定外部的執行環境, 以便在任何時候/任何地方都能執行. 為了向測試提供必需的資源, 這些資源應該由測試本身提供.

比如一個解析某類型文件的類, 可以把文件內容嵌入到測試代碼里, 在測試的時候寫入到臨時文件, 測試結束再刪除, 而不是從預定的地址直接讀取.

23. 權衡測試成本

不寫單元測試的代價很高, 但是寫單元測試的代價同樣很高. 要在這兩者之間做適當的權衡, 如果用執行覆蓋率來衡量, 業界標準通常在 80% 左右.

很典型的, 讀寫外部資源的錯誤處理和異常處理就很難達到百分百的執行覆蓋率. 模擬數據庫在事務處理到一半時發生故障并不是辦不到, 但相對于進行大范圍的代碼審查, 代價可能太大了.

24. 安排測試優先次序

單元測試是典型的自底向上過程, 如果沒有足夠的資源測試一個系統的所有??? 就應該先把重點放在較底層的???

25. 測試代碼要考慮錯誤處理

考慮下面的這個例子:

Handle handle = manager.getHandle();
assertNotNull(handle);

String handleName = handle.getName();
assertEquals(handleName, "handle-01");

如果第一個斷言失敗, 后續語句會導致代碼崩潰, 剩下的測試都無法執行. 任何時候都要為測試失敗做好準備, 避免單個失敗的測試項中斷整個測試套件的執行. 上面的例子可以重寫成:

Handle handle = manager.getHandle();
assertNotNull(handle);
if (handle == null) return;

String handleName = handle.getName();
assertEquals(handleName, "handle-01");

26. 寫測試用例重現 bug

每上報一個 bug, 都要寫一個測試用例來重現這個 bug (即無法通過測試), 并用它作為成功修正代碼的檢驗標準.

27. 了解局限

單元測試永遠無法證明代碼的正確性!!

一個跑失敗的測試可能表明代碼有錯誤, 但一個跑成功的測試什么也證明不了.

單元測試最有效的使用場合是在一個較低的層級驗證并文檔化需求, 以及 回歸測試: 開發或重構代碼時,不會破壞已有功能的正確性.

參考資料

[1] 維基百科關于單元測試的定義: Unit Testing

[2] 白盒和黑盒測試的簡短描述: What is black box/white box testing?

[3] 我們最常用的 C++ 單元測試框架: CxxTest

[4] 我們最常用的 Java 單元測試框架: TestNG

[5] 我們最常用的 C++ 覆蓋率分析工具: LCOV

[5] 我們最常用的 Java 覆蓋率分析工具: Cobertura

[5] 更多關于不允許訪問外部資源觀點: A Set of Unit Testing Rules

[6] 來自 Apple 的單元測試建議: Unit Test Guidelines

[7] JUnit 最佳實踐: JUnit best practices

譯者推薦中文資料

來自Google的單元測試技巧

淺談測試驅動開發 (TDD)

TDD/BDD會導致不完整的單元測試嗎?

Mock 不是測試的銀彈

TDD 推薦教程

單元測試的七種境界

(全文完) 歡迎關注『Java之道』微信公眾號
贊(5)
如未加特殊說明,此網站文章均為原創,轉載必須注明出處。天津11选5蛋托玩法 » 單元測試的基本準則
分享到: 更多 (0)

評論 3

  • 昵稱 (必填)
  • 郵箱 (必填)
  • 網址
  1. #1

    kekk點xyz 狠友福利

    紛淚雨2年前 (2018-04-27)回復
  2. #2

    加油

    逆向網賺2年前 (2018-05-07)回復
  3. #3

    yoyy點xyz 狠ff友ff福ff利

    夜未央2年前 (2018-06-05)回復

HollisChuang's Blog

聯系我關于我