自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何寫出有效的單元測試

原創(chuàng) 精選
開發(fā) 架構(gòu)
一個(gè)單元測試是一段自動化的代碼,這段代碼調(diào)用被測試的工作單元,之后對這個(gè)單元的單個(gè)最終結(jié)果的某些假設(shè)進(jìn)行校驗(yàn)。單元測試幾乎都是用單元測試框架編寫的;只要產(chǎn)品代碼不發(fā)生變化,單元測試的結(jié)果是穩(wěn)定的。

作者 | 王浩(光酒)

什么是單元測試

《單元測試的藝術(shù)》中對單元測試的定義:

一個(gè)單元測試是一段自動化的代碼,這段代碼調(diào)用被測試的工作單元,之后對這個(gè)單元的單個(gè)最終結(jié)果的某些假設(shè)進(jìn)行校驗(yàn)。單元測試幾乎都是用單元測試框架編寫的;只要產(chǎn)品代碼不發(fā)生變化,單元測試的結(jié)果是穩(wěn)定的。

為什么需要單元測試

在我看來,單元測試的意義可以總結(jié)如下三點(diǎn):

  • 單元測試是保證你寫的代碼是你想要的結(jié)果的最有效辦法
  • 單元測試幫我們塑造設(shè)計(jì)
  • 單元測試是最好的文檔之一?

單元測試描述了代碼的預(yù)期行為,可以最有效地保證代碼正確運(yùn)行,減少代碼缺陷;由于單元規(guī)模較小,當(dāng)因?yàn)榇a變更出現(xiàn)問題的時(shí)候,可以幫助我們快速定位問題;有單元測試覆蓋的代碼,讓我們更有信心,敢于放心做代碼重構(gòu);寫單元測試的過程往往伴隨著代碼重構(gòu),如果發(fā)現(xiàn)一段代碼單元測試很難寫,就需要反思我們的設(shè)計(jì),進(jìn)而重構(gòu)促進(jìn)代碼設(shè)計(jì)的優(yōu)化,幫助我們塑造設(shè)計(jì);同時(shí)單元測試也是一個(gè)最佳的、自動化的、可執(zhí)行的文檔;沒有單測覆蓋的代碼,是很難被維護(hù)的。

什么是有效的單元測試

可讀、可維護(hù)、可信賴、快速執(zhí)行!《單元測試的藝術(shù)》中描述優(yōu)秀單元的特性:

  • 它應(yīng)該是自動化的,可重復(fù)執(zhí)行;
  • 它應(yīng)該很容易實(shí)現(xiàn);
  • 它應(yīng)該第二天還有意義;
  • 任何人都應(yīng)該能一鍵運(yùn)行它;
  • 它應(yīng)該運(yùn)行速度很快;
  • 它的結(jié)果應(yīng)該是穩(wěn)定的(如果運(yùn)行之間沒有進(jìn)行修改的話,多次運(yùn)行一個(gè)測試應(yīng)該總是
  • 返回同樣的結(jié)果);
  • 它應(yīng)該能完全控制被測試的單元;
  • 它應(yīng)該是完全隔離的(獨(dú)立于其他測試的運(yùn)行);
  • 如果它失敗了,我們應(yīng)該很容易發(fā)現(xiàn)什么是期待的結(jié)果,進(jìn)而定位問題所在。

可讀性

“一般程序員寫得出計(jì)算機(jī)能讀懂的代碼。優(yōu)秀程序員寫得出人能讀懂的代碼” — 馬丁·福勒可讀的代碼才是可維護(hù)的;難以閱讀和理解的測試用例,最終的結(jié)果就是刪掉它,因?yàn)榫S護(hù)成本過高。可讀性高于純粹的性能。

可維護(hù)性

團(tuán)隊(duì)內(nèi)使用一套范式的結(jié)構(gòu),有助于使之更好用,快速定位問題;消滅代碼中的壞味道。

可信賴

可信賴的含義:

  • 測試可重復(fù);
  • 測試與依賴環(huán)境隔離;
  • 只測試不進(jìn)行驗(yàn)證是不可靠的測試;
  • 在測試類中不要依賴與測試的順序;
  • 測試的結(jié)果是精準(zhǔn)的:校驗(yàn)的精準(zhǔn)以及錯(cuò)誤問題的精準(zhǔn)定位;

快速執(zhí)行

保證單測快速執(zhí)行,縮短反饋時(shí)長;

為什么有效的單元測試如此重要

無效的單元測試是沒有意義的,反而會增加維護(hù)成本,最終導(dǎo)致單元測試的失敗!

圖片

如上圖所示,坐標(biāo)中任意一個(gè)點(diǎn),其與橫縱坐標(biāo)垂直線所形成的矩形面積代表CI為團(tuán)隊(duì)帶來的價(jià)值,那么在我看來有兩個(gè)關(guān)鍵的因素:橫坐標(biāo)是單元測試的基礎(chǔ)能力建設(shè),縱坐標(biāo)則是有效的單元測試;沒有有效的單元測試,基礎(chǔ)能力做出花來也毫無意義!完善的基礎(chǔ)能力同時(shí)也幫助我們更低成本的寫出有效的單元測試。

如何寫有效的單元測試

我們以Flutter為例,來一起討論如何寫有效的單元測試;

使用測試框架

Flutter官方提供的測試框架:

  • flutter_test
  • integration_test

統(tǒng)一的編碼約定

不論是AAA(Arrange-Act-Assert)還是GWT(Given-When-Then),統(tǒng)一的編碼約定幫助保證測試代碼的可讀性、可維護(hù)性。

圖片

使用測試替身

測試替身幫助我們隔離被測試代碼,加速執(zhí)行速度,保證測試代碼是可信賴的;

圖片

  • Dummy:一種什么也不做的實(shí)現(xiàn)方式。接口中的每個(gè)方法什么也不做,如果方法有返回值,返回的值盡量接近null或者0。
  • Stub:Dummy的一種,Stub的函數(shù)并不返回null或0,而是返回能推動函數(shù)沿預(yù)定路徑被測試的值。
  • Spy:Stub的一種,它返回測試所需的特定值,推動系統(tǒng)沿著我們期望的路徑前行。然而,Spy能記住對它所做的事,并允許測試詢問。
  • Mock:Spy的一種,它返回測試所需的特定值,推動系統(tǒng)沿著我們期望的路徑前行,而且還會記住對它所做的事。不過,Mock還知道我們的預(yù)期,基于這些預(yù)期,判斷測試是否通過;換而言之,Mock中寫明了測試斷言。
  • Fake:Fake是一種模擬器,它實(shí)現(xiàn)基礎(chǔ)業(yè)務(wù)規(guī)則,這樣測試就能要求該Fake按需要的路徑執(zhí)行。

圖片

一個(gè)測試應(yīng)當(dāng)只檢查一件事

明確測試意圖,一旦出錯(cuò)可以精準(zhǔn)定位問題;

圖片

一個(gè)測試只有一個(gè)模擬對象

避免過多模擬對象,一個(gè)測試用例的校驗(yàn)內(nèi)容盡量簡單;

避免冗余測試

冗余測試會提高維護(hù)成本;

圖片

避免條件邏輯

條件邏輯會讓你的單元測試更難以維護(hù),出問題不容易排查,不夠精準(zhǔn);

圖片

單測需要確定性

避免脆弱測試,Mock不確定的依賴:時(shí)間、隨機(jī)數(shù)、并發(fā)性、基礎(chǔ)設(shè)施、現(xiàn)存數(shù)據(jù)、持久化、網(wǎng)絡(luò)等等;

圖片

測試快速執(zhí)行

避免sleep等操作,導(dǎo)致測試執(zhí)行緩慢;圖片

避免過度指定

對于過度指定的討論,其核心問題就是要我們判斷哪些是單元測試應(yīng)該覆蓋的,哪些是應(yīng)該留給其他測試手段的。如果一個(gè)場景,單元測試覆蓋之后,導(dǎo)致經(jīng)常單測失敗,需要不斷更新維護(hù),那就可以考慮不做單元測試覆蓋。像素完美是一個(gè)典型的、經(jīng)常拿出來討論的例子,F(xiàn)lutter的Golden Test就是一個(gè)golden master testing的例子;《有效的單元測試》中關(guān)于像素完美的討論:

像素完美:顧名思義,是一種特定于圖形和圖像生成的測試壞味道。它混雜了魔法數(shù)字和基本斷言,使得測試極難閱讀也極其脆弱。

這種測試幾乎無法閱讀,因?yàn)榧词箿y試在語義上是處于高層概念的,卻仍然會針對硬編碼的底層細(xì)節(jié)例如像素坐標(biāo)和顏色來進(jìn)行斷言。指定坐標(biāo)上的像素是黑還是白,與兩個(gè)圖形是否相連或堆疊的概念是有區(qū)別的。

這種測試極其脆弱,因?yàn)榧词购苄〉暮筒幌嚓P(guān)的輸入變化——是否是另一個(gè)圖像,或圖形對象的渲染方式——都足以影響輸出、打破測試,誰讓你非要精確地檢查像素坐標(biāo)和顏色呢。同樣的問題在采用golden master技術(shù)時(shí)也會遇到,其做法是事先將圖像錄制下來,并手工檢查其正確性,以后再進(jìn)行測試時(shí)就將渲染出的圖像與之進(jìn)行比對。

這些可不是我們愿意去維護(hù)的測試。我們不希望帶著這種脆弱的精確度去編寫測試,而是使用模糊匹配和智能算法來代替繁瑣的數(shù)值比較。

對于特定場景,Golden Test是一個(gè)非常有效的手段,但需要非常謹(jǐn)慎的評估;慎用Golden Test!

圖片

不要寫永不失敗的測試,不要寫沒有校驗(yàn)的測試

單測需要對明確的邏輯校驗(yàn),永不失敗的測試或者沒有校驗(yàn)的測試是不可信賴的。

圖片

測試不要名不副實(shí)

避免測試的描述與測試內(nèi)容不符;測試結(jié)果必須精準(zhǔn);測試該失敗的時(shí)候一定要失敗!

圖片

測試私有或者受保護(hù)的方法

解決思路:

  • 將方法變成公共方法;
  • 將方法抽取到新類;
  • 將方法變成靜態(tài)方法;
  • 將方法成為測試可見方法;

避免強(qiáng)制的測試順序

依賴測試順序?qū)е聹y試可靠性變得脆弱,未來維護(hù)成本變高;

圖片

清理測試環(huán)境

在teardown階段清理測試環(huán)境,例如還原全局的Config、清理創(chuàng)建的文件目錄等等;

圖片

統(tǒng)一的單測命名、變量命名

統(tǒng)一的單測命名可以提高可讀性、可維護(hù)性;

使用有意義的斷言

斷言的錯(cuò)誤信息要有意義,出現(xiàn)問題能夠明確錯(cuò)誤的原因;

圖片

把單元測試視為“一等公民”

測試用例應(yīng)該被視為“一等公民”:同樣需要代碼評審,同樣需要代碼質(zhì)量檢查,確保單元測試的有效性;單元測試代碼評審的過程,也是團(tuán)隊(duì)同學(xué)互相學(xué)習(xí)的過程,沉淀最佳實(shí)踐的過程。

加速執(zhí)行速度

日常對單測執(zhí)行時(shí)間進(jìn)行監(jiān)控,對測試進(jìn)行性能分析,優(yōu)化執(zhí)行時(shí)間過長的測試用例。

測試金字塔

測試金字塔是Mike Cohn在他的著作《Succeeding with Agile》一書中提出了這個(gè)概念。測試金字塔是一個(gè)比喻,它告訴我們要把軟件測試按照不同粒度來分組。它也告訴我們每個(gè)組應(yīng)該有多少測試。

圖片

為了維持金字塔形狀,一個(gè)健康、快速、可維護(hù)的測試組合應(yīng)該是這樣的:寫許多小而快的單元測試。適當(dāng)寫一些更粗粒度的測試,寫很少高層次的端到端測試。注意不要讓你的測試變成冰淇淋或者沙漏那樣子,這對維護(hù)來說將是一個(gè)噩夢,并且跑一遍也需要太多時(shí)間。

圖片

避免測試重復(fù)

在實(shí)現(xiàn)測試金字塔時(shí),你也應(yīng)該牢記這兩條基本法則:

如果一個(gè)更高層級的測試發(fā)現(xiàn)了一個(gè)錯(cuò)誤,并且底層測試全都通過了,那么你應(yīng)該寫一個(gè)低層級測試去覆蓋這個(gè)錯(cuò)誤;

竭盡所能把測試往金字塔下層趕;

如果你已經(jīng)在低層級測試?yán)锔采w了所有情況,那么再維護(hù)一個(gè)高層級的測試就沒有必要了。警惕沉沒成本的思維陷阱,果斷摁下刪除鍵。沒有理由在不再提供價(jià)值的測試上浪費(fèi)寶貴時(shí)間。

補(bǔ)充單元測試應(yīng)該從哪里開始

單元測試應(yīng)該及時(shí)編寫,就算沒有實(shí)踐TDD,也應(yīng)該在代碼實(shí)現(xiàn)之后盡快編寫單元測試,避免寫出不可測試的代碼,也可以讓bug盡早暴露;但很不幸的,我們很多時(shí)候在剛開始卓越工程,推廣單元測試的時(shí)候,不得不面對補(bǔ)充單元測試的情況;這絕對是一個(gè)有挑戰(zhàn)的事情。補(bǔ)充單元測試應(yīng)該從哪里開始?參考測試金字塔,對于基礎(chǔ)組件庫來說,可以根據(jù)具體情況來定;對于業(yè)務(wù)庫來說,第一步建議從金字塔頂端的測試:

  • 優(yōu)先覆蓋回歸測試用例中P0級別的用例;
  • 避免過度指定的端到端測試;
  • 適當(dāng)?shù)钠跫s測試;

接下來,從金字塔中間層開始,不斷向上、向下補(bǔ)充;

可測試的設(shè)計(jì)

應(yīng)當(dāng)容易、快速地為一段代碼編寫單元測試;可測試的設(shè)計(jì),使我們寫出模塊化的設(shè)計(jì);

行動指南

為了寫出可測試的代碼,需要注意以下幾點(diǎn):

  • 避免復(fù)雜的私有方法;
  • 避免final方法;
  • 避免static方法;
  • 使用new要當(dāng)心;
  • 避免構(gòu)造函數(shù)中包含邏輯;
  • 避免單例;
  • 組合優(yōu)于繼承;
  • 避免服務(wù)查找;
  • 基于接口的設(shè)計(jì);

可測試的代碼是否違背了SOLID中的開閉原則?

可測試的代碼設(shè)計(jì),有的時(shí)候需要避免復(fù)雜的私有方法或者受保護(hù)的方法,因?yàn)檫@些意味著不可測試;這樣的話是不是意味著可測試的設(shè)計(jì)違反了開閉原則呢?在代碼重構(gòu)的時(shí)候,可以認(rèn)為給對象模型增加了另外一種最終用戶——測試用戶。另外如果一部分代碼實(shí)在不希望暴露,也可以使用@visibleForTesting 修飾;

單元測試與重構(gòu)

寫單元測試的過程往往伴隨著重構(gòu);代碼重構(gòu)同樣需要單元測試保證代碼正確運(yùn)行。重構(gòu)需要遵守的紀(jì)律:無測試重構(gòu)無意義,頻繁重構(gòu)、果斷重構(gòu)、堅(jiān)決重構(gòu);

持續(xù)重構(gòu)

將麻煩扼殺在搖籃;

果斷重構(gòu)

敏捷編程的名言之一。規(guī)則很簡單:重構(gòu)時(shí)要勇敢。勇敢嘗試,勇敢修改,不用害怕代碼。

讓測試始終能通過

建一個(gè)綠色安全區(qū),不允許破窗出現(xiàn)。

留條出路

倉庫打好tag,以便在需要的時(shí)候能夠回滾。

可測試的代碼

可測試的代碼就是解耦了的代碼;可測試的代碼幫助我們實(shí)現(xiàn)更好的抽象。

做不到TDD,可以做到測試先行

下圖是遵循TDD三大法則的實(shí)踐過程;TDD很強(qiáng)大,但不一定適用所有的團(tuán)隊(duì),推廣難度很大,學(xué)習(xí)曲線很高。

圖片

TDD事實(shí)上由兩個(gè)方面組成:測試先行,以及演進(jìn)式設(shè)計(jì);測試先行是非常重要的工程實(shí)踐,做不到TDD,可以做到測試先行。在Kent Beck的經(jīng)典名著《解析極限編程》中,提到:盡早測試,經(jīng)常測試,自動測試!測試先行的本質(zhì)能力要求是接口的設(shè)計(jì)能力——能否清晰的定義出設(shè)計(jì)單元的邊界。

如何理解單元測試代碼覆蓋率

不要把它們變成管理的指標(biāo)。這就是你使用覆蓋率數(shù)字的目的:使用它們作為衡量標(biāo)準(zhǔn)來幫助你改進(jìn),而不是用它們作為懲罰團(tuán)隊(duì)和使構(gòu)建失敗的棍棒。 ——《匠藝整潔之道》

代碼覆蓋率的一大忌諱:為了追求代碼覆蓋率,只測試不進(jìn)行驗(yàn)證;一味追求代碼覆蓋率,往往寫出無效的單元測試,額外增加了維護(hù)成本,最終不得不放棄以失敗告終。與其追求代碼覆蓋率,不如將重點(diǎn)關(guān)注在確保寫出有意義的測試。

沉淀最佳實(shí)踐

必須承認(rèn)單元測試有一定的成本,成本曲線來看,前期比較高;恰恰是這前期的門檻,讓很多人望而卻步。在團(tuán)隊(duì)內(nèi)推廣的時(shí)候,最難的就是寫出第一個(gè)單元測試;我們需要沉淀最佳實(shí)踐,幫助降低寫單元測試的成本,讓我們更容易地寫出有效的單元測試。我覺得沉淀最佳實(shí)踐最好的方法,就是Code Review;正如我們前面所說的,要把單元測試當(dāng)成是“一定公民”,在Code Review的過程中,互相學(xué)習(xí)、分享最佳實(shí)踐,消除無效的單元測試。

隔離單元測試與集成測試

集成測試是對一個(gè)工作單元進(jìn)行的測試,這個(gè)測試對被測試的工作單元沒有完全的控制,并使用該工作單元一個(gè)或多個(gè)真實(shí)依賴,例如時(shí)間、網(wǎng)絡(luò)、數(shù)據(jù)庫、線程或隨機(jī)數(shù)生成器等。

任何測試,如果它的運(yùn)行速度不快,結(jié)果不穩(wěn)定,或者要用到被測試單元的一個(gè)或多個(gè)真實(shí)依賴,就是集成測試。在日常開發(fā)過程,我們需要建一個(gè)綠色安全區(qū):單元測試與集成測試隔離;集成測試不夠穩(wěn)定,運(yùn)行時(shí)間長等問題,如果不做隔離,日常開發(fā)浪費(fèi)時(shí)間和精力維護(hù),最后導(dǎo)致開發(fā)人員不再信任測試。

單元測試與ABTest

單元測試與ABTest有什么關(guān)系嗎?事實(shí)上沒有什么關(guān)系。但一定程度程度上,它們本質(zhì)是相同的,都是保障線上代碼質(zhì)量(當(dāng)然單測的成本,對于基建、開發(fā)者的能力的要求更高);在日常開發(fā)中,經(jīng)常主動為新的代碼邏輯增加AB開關(guān),一旦線上出問題留一條后路;發(fā)生問題的時(shí)候往往感慨AB開關(guān)救我一命;單元測試可以讓問題左移,防止問題上線,同樣是一道保護(hù);如果有一天團(tuán)隊(duì)同學(xué)愿意主動增加單元測試來保護(hù)自己的代碼,那么單元測試這件事就算比較成功了。

寫在最后

從軟件工程到卓越工程,單元測試從可選變成了必要;想要實(shí)現(xiàn)主干開發(fā)、大庫模式,單元測試是前提條件。關(guān)于單元測試這件事,我覺得最重要永遠(yuǎn)是寫單元測試的人,優(yōu)秀的團(tuán)隊(duì)文化非常重要,沒有什么能夠真正衡量單元測試做的好壞,有的只是程序員的職業(yè)操守。我們花了很大的篇幅討論有效單元測試的重要性以及如何寫出有效的單元測試,不得不承認(rèn)單元測試有一定的成本,真正實(shí)踐依然需要很多的路要走,需要我們在實(shí)踐中定義好單元測試的邊界,找到最適合團(tuán)隊(duì)的最佳實(shí)踐。

參考文檔

  • 《單元測試的藝術(shù)》
  • 《有效的單元測試》
  • 《Succeeding with Agile》
  • 《匠藝整潔之道》
  • The Test Pyramid:https://martinfowler.com/articles/practical-test-pyramid.html
  • Software Engineering at Google:https://qiangmzsx.github.io/Software-Engineering-at-Google/#/zh-cn/Chapter-12_Unit_Testing/Chapter-12_Unit_Testing
責(zé)任編輯:武曉燕 來源: 阿里開發(fā)者
相關(guān)推薦

2020-09-30 08:08:15

單元測試應(yīng)用

2021-06-15 08:08:47

Java單元測試

2017-01-14 23:42:49

單元測試框架軟件測試

2011-04-18 13:20:40

單元測試軟件測試

2022-08-11 16:37:55

單元測試代碼

2011-11-30 22:03:49

ibmdwJava

2023-07-26 08:58:45

Golang單元測試

2021-03-28 23:03:50

Python程序員編碼

2011-05-16 16:52:09

單元測試徹底測試

2022-03-15 11:55:24

前端單元測試

2020-07-15 08:17:16

代碼

2017-01-14 23:26:17

單元測試JUnit測試

2017-01-16 12:12:29

單元測試JUnit

2011-06-14 15:56:42

單元測試

2020-08-18 08:10:02

單元測試Java

2022-05-12 09:37:03

測試JUnit開發(fā)

2017-03-23 16:02:10

Mock技術(shù)單元測試

2021-05-05 11:38:40

TestNGPowerMock單元測試

2024-10-16 16:09:32

2011-07-04 18:16:42

單元測試
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號