單元測試之如何編寫優(yōu)秀的單元測試用例

這篇文章是結(jié)合"The Art of Unit Testing"一書及業(yè)內(nèi)一些單元測試大牛們的總結(jié)整理而成。主要內(nèi)容是實(shí)際工作中如何運(yùn)用一些技巧和方法編寫出可靠、可讀、可維護(hù)的優(yōu)秀單元測試用例。

優(yōu)秀單元測試的定義

單元測試:一段** 自動化** 的代碼 ,這段代碼調(diào)用被測試的** 工作單元 ,之后對這個工作單元的 單個最終結(jié)果 的某些假設(shè)進(jìn)行檢驗(yàn)。單元測試幾乎都是用 單元測試框架進(jìn)行編寫。單元測試容易編寫,快速運(yùn)行可自動化,可靠可讀,可維護(hù),結(jié)果穩(wěn)定**。

集成測試:對一個工作單元進(jìn)行的測試,這個測試對被測試的工作單元沒有完全的控制,并使用該單元的一個或多個真實(shí)依賴物,例如數(shù)據(jù)庫、系統(tǒng)時間、系統(tǒng)文件等

工作單元:從調(diào)用系統(tǒng)一個公共方法到產(chǎn)生一個測試可見的最終結(jié)果,其間這個系統(tǒng)發(fā)生的行為。一個工作單元既可以是小到只包含一個方法,也可以大到包含實(shí)現(xiàn)某功能的多個類或方法。

最終結(jié)果:是指被調(diào)用的公共方法返回一個值;或者在方法調(diào)用前后,系統(tǒng)的狀態(tài)或行為有可見的變化,這種變化不需要查詢私有狀態(tài)即可判斷;又或者是** 調(diào)用了一個不受測試控制的第三方系統(tǒng) **,這個第三方系統(tǒng)不返回任何值,或者返回值已被忽略。

編寫可靠的測試

所謂可靠性是指單元測試本身是正確的,即該失敗的時候失敗,該成功的時候成功。只有保證單元測試的可靠性,才能讓開發(fā)人員信任該單元測試,不會為了以防萬一進(jìn)行調(diào)試等其他工作。
在"單元測試的藝術(shù)"中,作者給出了一些簡單的原則和技術(shù)幫助編寫可靠的測試。

  • 依據(jù)實(shí)際情況合理地刪除或修改單元測試
    如果確定是測試缺陷,而不是產(chǎn)品缺陷(被測試代碼缺陷)時,需要立刻修改相關(guān)單元測試代碼;如果被測試的產(chǎn)品代碼的語義或者API變更導(dǎo)致測試失敗,這時是需要修改測試,使用新的語義;如果看到測試名含義不清或者單元測試的可維護(hù)性差就應(yīng)該在保證單元測試基本功能前提下修改測試名稱或者重構(gòu)測試;如果同一個功能多個單元測試,請刪除重復(fù)測試。
  • 避免在單元測試代碼中包含邏輯
    包含邏輯的測試是指測試代碼中包含switch、if/else、for/while等控制流語句。這樣的測試可讀性差,代碼脆弱,測試代碼的復(fù)雜度高,容易包含缺陷,測試結(jié)果不容易重現(xiàn)。
  • 每個單元測試只測試一個關(guān)注點(diǎn)
    所謂的一個關(guān)注點(diǎn)就是指一個工作單元的一個最終結(jié)果:一個返回值、系統(tǒng)狀態(tài)的一個改變、對第三方對象的一個調(diào)用。測試多個關(guān)注點(diǎn)一方面不利于測試命名,另一方面很多單元測試框架中,一個失敗斷言就會拋出一個特殊類型的異常,后面代碼不會繼續(xù)執(zhí)行,這樣不利于收集測試失敗原因。
  • 區(qū)分單元測試和集成測試
  • 用代碼審查確保代碼覆蓋率
    如果你做了代碼審查、測試審查、確保測試優(yōu)秀而且覆蓋了所有代碼,那么就可以避免犯簡單愚蠢的錯誤,同時也可以從持續(xù)的學(xué)習(xí)中獲益。

編寫可讀的測試

單元測試可以看做是一個婉婉道來的故事,這個故事是講給下一代開發(fā)者聽,故事的內(nèi)容就是這個應(yīng)用程序的組成及其流程。既然是故事,就一定要形象生動,易于理解。那么可讀性就是指如何確保其他開發(fā)者能夠理解他們要做的工作,以便維護(hù)產(chǎn)品代碼和測試。
單元測試的可讀性其實(shí)和代碼可讀性在很多方面類似,下面就逐一討論這些方面。

  • 單元測試的命名標(biāo)準(zhǔn)
    合理地命名測試,主要目的是為了使后來的開發(fā)者從為了理解測試而閱讀代碼的負(fù)擔(dān)中解脫出來。測試名應(yīng)該包含三部分:被測試方法名、測試場景(即測試使用的條件)、預(yù)期行為(即被測試方法的最終結(jié)果)。
  • 單元測試中的變量命名規(guī)范
    單元測試除了主要的測試功能之外,它還為API提供某種形式的文檔。通過合理命名變量,幫助閱讀測試的人可以盡快理解你要驗(yàn)證什么(從而更加理解產(chǎn)品代碼中想要實(shí)現(xiàn)什么功能)。
  • 斷言和操作分離
  • 避免濫用setup和teardown
    比如在setup中準(zhǔn)備stub和mock對象,這種情況就會導(dǎo)致閱讀測試的人意識不到測試中使用了模擬對象,也不知道這些模擬對象預(yù)期是什么。

編寫可維護(hù)的測試

可維護(hù)性是大多數(shù)單元測試面臨的最大挑戰(zhàn)。隨著時間累計(jì),單元測試似乎越來越難維護(hù)和理解,被測代碼一個微小的改動,似乎都會使某個測試失敗。那么怎么能夠盡量降低可維護(hù)性的成本呢?

  • 只測試公共契約,避免測試私有或者受保護(hù)的方法
    私有方法可以看做是系統(tǒng)內(nèi)部契約,這個內(nèi)部契約是動態(tài),在系統(tǒng)重構(gòu)時可能會被隨時修改,因此針對這些內(nèi)部契約的單元測試也很可能會失敗。而內(nèi)部契約最終都會被一個公共契約(公共方法、整體功能)所調(diào)用,也就是說任何私有方法通常都是一個更大的工作單元的一部分。
  • 去除重復(fù)代碼
    可以使用輔助方法或者setup來去除重復(fù)代碼的問題
  • 實(shí)施測試隔離
    測試隔離是指每個測試都只生活在自己的小世界中,它與其他測試之間沒有任何依賴關(guān)系,甚至不知道其他測試存在。
    下面列舉幾種常見的測試隔離的反模式。
    1、測試結(jié)果依賴測試執(zhí)行的順序
    2、測試調(diào)用其他測試方法
    3、測試中使用的共享資源(內(nèi)存或外部資源)沒有得到清理或回滾
  • 避免對不同關(guān)注點(diǎn)多次斷言,盡量使用參數(shù)化測試或者對每個關(guān)注點(diǎn)設(shè)計(jì)單獨(dú)的測試用例
  • 避免過度指定
    常見的過度指定的例子。
    1.對系統(tǒng)內(nèi)部契約進(jìn)行斷言
    2.使用過多的模擬對象
    3.精確匹配
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 0x00 單元測試Pro & Con 最近嘗試在我參與的游戲項(xiàng)目中引入TDD(測試驅(qū)動開發(fā))的開發(fā)模式,因此單元測...
    陳嘉棟閱讀 942評論 0 3
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,733評論 25 709
  • 在你不知道的時間里愛著你。
    Mooben閱讀 167評論 0 0
  • 現(xiàn)在讓我們來做一個打開自己的練習(xí),把那些我們想要帶進(jìn)我們生命的能量先給出去。我把它叫做“蓮花冥想”,這是一個非常有...
    道心禪閱讀 3,663評論 0 3

友情鏈接更多精彩內(nèi)容