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

聊聊為什么需要單元測試?

開發(fā) 測試
本文討論了單元測試的必要性以及單元測試中的一些重點注意實現(xiàn)。

沒有單元測試時的驗證

在學(xué)習(xí)編程和業(yè)務(wù)開發(fā)的工程中,我們有一段時間總是在討論:單元測試是否有用?而進(jìn)行這種討論的主要原因是,我們似乎在不使用單元測試的時候,項目也可以跑得很好。小到畢業(yè)設(shè)計時的內(nèi)容,大到一個十幾人大小的團(tuán)隊。我們設(shè)計項目、分析需求,然后根據(jù)設(shè)計的結(jié)果進(jìn)行代碼的編寫,然后進(jìn)行接口或者業(yè)務(wù)執(zhí)行上面的測試,讓我們知道所編寫的代碼已經(jīng)可以完美的完成計劃內(nèi)容后,會請測試同學(xué)幫我們進(jìn)行代碼測試,以保證他們確實完成了計劃中的內(nèi)容。最終,代碼上線,可喜可賀。

看起來沒什么不好的,直到最終的問題發(fā)生。

我們當(dāng)然會在開發(fā)的時候進(jìn)行項目功能的測試,常用手段諸如用main對指定的代碼塊驗證,或者使用postman對我們設(shè)計的接口進(jìn)行測試驗證?;蛟S中間還存在了一些數(shù)據(jù)庫的修改,比如模擬下單數(shù)據(jù),或者模擬用戶注冊。

這些方法是一定程度上可以完成當(dāng)期內(nèi)的功能需求的,否則也不會有那么多的“單元測試真的有用嗎”的這種聲音。那么問題是什么呢?

問題是你無法永遠(yuǎn)保證“當(dāng)期的業(yè)務(wù)測試”就是能覆蓋你本期提供的功能點,以及即便是測試同學(xué)保存有以往所有測試用例的自動化測試內(nèi)容,也無法真正的保證你的系統(tǒng)是完好的,因為業(yè)務(wù)功能和軟件功能中間是有隔閡的。

盡管是搞笑圖,但是精準(zhǔn)的命中了我要說的內(nèi)容:

針對用例設(shè)計的功能測試,無法保證你的“系統(tǒng)”正常。

測試驅(qū)動開發(fā)

我們一般在項目開發(fā)中進(jìn)行的功能測試,是可以保證當(dāng)期中業(yè)務(wù)流程。但是即便功能測試的過程包含了所有的過往功能,也只能保證業(yè)務(wù)流程是正確的,不能保證你的設(shè)計在未來的擴(kuò)展中是正確的(舉例就是業(yè)務(wù)可能只需要正常流程,但是沒有異常流程的需求)。

所以,如果要代碼能實現(xiàn)所有計劃的功能,就要由開發(fā)者來編寫其對應(yīng)的測試模塊。因為是開發(fā)人員,所以知道自己的所有邏輯組合是什么,而根據(jù)這種需求編寫的測試代碼則可以長久地對你的系統(tǒng)進(jìn)行測試。而這就是就是:

TDD(測試驅(qū)動開發(fā))

測試驅(qū)動開發(fā)中最主要的準(zhǔn)則是:

在編寫業(yè)務(wù)代碼之前先編寫單元測試。

這條準(zhǔn)則的目的在于:不要編寫沒有單元測試的代碼。實際上我們在編寫功能業(yè)務(wù)的時候,一般都會假設(shè)一個入口,然后再經(jīng)過一段邏輯處理之后,最終返回一個結(jié)果。而先編寫單元測試的目的,就是先將你的這個假設(shè)直接落地到代碼中,那么在后續(xù)的編程過程中你就可以忽略這部分的假設(shè),專注于邏輯編寫,甚至即使你最后忘記了之前的假設(shè)也不要緊,因為你已經(jīng)將他寫道代碼中了。

而這一準(zhǔn)則的進(jìn)一步拆解,可以將其細(xì)化為三個準(zhǔn)則:

  1. 在編寫不能通過的單元測試前,不可以編寫生產(chǎn)代碼。
  2. 只可編寫剛好不能通過的單元測試,不能編譯也算。
  3. 只可編寫剛好足以通過當(dāng)前失敗測試的生產(chǎn)代碼。

根據(jù)細(xì)分的這三個準(zhǔn)則,我們可以將我們編寫一個邏輯的的步驟變成:寫一個剛好失敗的單元測試,然后用剛好滿足邏輯的生產(chǎn)代碼滿足它。這樣一個小循環(huán)可能只是在一兩分鐘內(nèi)就進(jìn)行一次。而在IDEA等現(xiàn)代IDE的幫助下,可以在test包下的同包路徑創(chuàng)建對應(yīng)的測試方法,大大加快了單元測試的編寫時間。而通過剛好的異常,讓每一次的業(yè)務(wù)邏輯得到了控制,通過剛好滿足則讓每一次生產(chǎn)代碼的編寫不會過度發(fā)散。因為如果你對生產(chǎn)代碼過度設(shè)計,那么你也需要對應(yīng)的單元測試代碼來保證你設(shè)計的的得當(dāng)性。

如果按這種循環(huán)進(jìn)行編寫,則我們在編寫業(yè)務(wù)代碼的同時只需要多十幾秒就可以完成單元測試的編寫。而單元測試可以完整地覆蓋業(yè)務(wù)單元元。但是隨著業(yè)務(wù)代碼的增加,測試代碼的數(shù)量也將急劇增加,其對應(yīng)的管理也是一種挑戰(zhàn)。

系統(tǒng)進(jìn)化的保證

回到最開始的例子,我們說在一些團(tuán)隊中,我們總是覺得單元測試是低效的,會影響業(yè)務(wù)的上線速度。我們也說這種方法看起來沒什么不好的,直到最終的問題發(fā)生。而這最終的問題就是:重構(gòu)。

這里說的重構(gòu)并不一定是大范圍的整體系統(tǒng)重構(gòu)。我們在之前的文章《如何阻止軟件退化》中提到:要保持軟件設(shè)計質(zhì)量不退化,必須要在每次需求變更的時候,根據(jù)變更點調(diào)整原有程序的設(shè)計結(jié)構(gòu)。

而當(dāng)我們相對原有的程序結(jié)構(gòu)進(jìn)行調(diào)整的時候,我們無法確保對代碼的改動能如預(yù)期的工作,也無法保證系統(tǒng)中的某個修改點是否會影響到系統(tǒng)的其他部分。舉個例子:當(dāng)你會支付的路由進(jìn)行修改的時候,如果出現(xiàn)意外則會導(dǎo)致其他支付方式的失敗,但是如果確保功能正常則你需要將所有的支付邏輯都進(jìn)行一遍功能測試,而仍然可能存在功能點的遺漏(這個是親身經(jīng)歷)。因為害怕新增加的功能會帶來更多的bug導(dǎo)致加班,最終的結(jié)論就是我們可能會抗拒對功能結(jié)構(gòu)的調(diào)整,而變成所謂的“屎上雕花”。所以從這個角度上來說,如果沒有單元測試,則軟件將不可避免地直線的退化。

而相反地來說,如果我們的系統(tǒng)包含單元測試。我們才不用擔(dān)心對于代碼的修改,每一次調(diào)整都能通過那些“剛好”的單元測試,那么不論你如何進(jìn)行設(shè)計模式的重構(gòu),都不用擔(dān)心引入新的不可預(yù)知的缺陷。

所以當(dāng)有了單元測試,才能讓我們的系統(tǒng)有了進(jìn)一步的維護(hù)性、擴(kuò)展性,也才有了系統(tǒng)進(jìn)化的可能。

應(yīng)該被重視的單元測試

我們需要單元測試來保證系統(tǒng)功能的擴(kuò)展性、可維護(hù)性。但是這并不意味著,只要有單元測試就可以。事實上我們應(yīng)當(dāng)如同生產(chǎn)代碼一樣的重視單元測試。原因很簡單:

單元測試代碼同樣會隨著功能調(diào)整而變得腐化。

而如果當(dāng)它腐化的難以維護(hù)的時候,誰都不會愿意去修改它。最終的結(jié)果就是我們不用單元測試了,然后失去了代碼的擴(kuò)展性。所以測試代碼必須要隨業(yè)務(wù)代碼的修改而同步修改,并不能因為單元測試只運行在測試環(huán)境而輕視單元測試的編寫,我們同樣需要讓單元測試的代碼也足夠整潔,讓其便于維護(hù)。

測試的邏輯

單元測試的時候重要的是體現(xiàn)出當(dāng)前進(jìn)行的測試內(nèi)容,而讓別人理解測試內(nèi)容的重中之重是測試“可讀性”。如果單元測試的代碼中充滿的一長串的業(yè)務(wù)邏輯或者斷言內(nèi)容,那么讀起來就會十分的費勁。為了避免開發(fā)人員淹沒在代碼的細(xì)節(jié)中,有一種較為公認(rèn)的單元測試的構(gòu)造方法:構(gòu)造-操作-檢驗(BUILD-OPERATE-CHECK),并使用give-when-then的命名方式來進(jìn)行命名。

舉一個例子(這里直接用的CLEAN CODE的例子了):

givenPages(xxx);
whenRequestIsIssued(xxx);
thenResponseShouldBeXML();

其中,第一部分將構(gòu)造測試數(shù)據(jù)的內(nèi)容分裝到given開頭的方法中;第二部分將操作測試數(shù)據(jù)的內(nèi)容封裝到when開頭的方法中;第三部分將檢查操作是否得到預(yù)期的結(jié)果封裝到then開頭的方法中。

這樣就屏蔽了絕大部分的代碼細(xì)節(jié),并用方法的名稱直接描述了測試的前置條件、處理過程、判斷結(jié)果。同時當(dāng)我們涉及到一些復(fù)雜流程的判斷的時候,我們是可以單獨為單元測試來編寫一部分額外的方法來支撐單元測試。這樣可以讓人變這樣可以讓人快速地理解單元測試的邏輯。

可以放松的部分

盡管說我們需要讓單元測試保持代碼保持整潔,并需要向生產(chǎn)代碼一樣地重視它。但并不意味著我們的測試代碼和生產(chǎn)代碼的準(zhǔn)則是完全一樣的。因為單元測試的準(zhǔn)則是具有可讀性的代碼并能精準(zhǔn)地描述關(guān)注的測試功能邊界。

所以有一些內(nèi)容是不需要和生產(chǎn)代碼保持一致的。其中最明顯的就是性能要求。

我們在線上代碼中需要對系統(tǒng)性能進(jìn)行各種優(yōu)化,但是單元測試的代碼是跑在測試環(huán)境中并且單個邏輯每次只執(zhí)行一遍,對單元測試來說,0.1ms的邏輯和1ms的邏輯差距可能并不明顯。這樣的情況下我們可能會選用一下表達(dá)能力更強(qiáng)的方法來進(jìn)行項目的編寫比如使用"+"號對字符串進(jìn)行拼接,我們一般都會用StringBuilder,但是不得不說直接使用“+”拼接的實現(xiàn)可讀性更高一點。除此之外還有一些異步的功能可以使用串行化來校驗,以便校驗每一步的結(jié)果。

單一概念

為了保證每一個單元測試中邏輯的可讀性,所以我們希望每一個單元測試只對一個概念進(jìn)行測試,這樣就可以用一組give-when-then的方法來對這個測試概念進(jìn)行描述。當(dāng)我們發(fā)現(xiàn)單元測試存在多個概念的時候就會將他們拆開分別進(jìn)行測試。這樣就避免了多個概念聚合在一個單元測試方法中的時候,會猶豫復(fù)合概念導(dǎo)致掩蓋了一些遺漏的測試點在其中。同時也保證了單元測試的可讀性。

其他原則

除此之外,單元測試還要保證:

  • 快速性:單元測試可快速執(zhí)行,支持頻繁測試。
  • 獨立性:單元測試不互相依賴,隨時以任意順序執(zhí)行。
  • 可重復(fù)性: 單元測試可以反復(fù)執(zhí)行且結(jié)果統(tǒng)一,否則永遠(yuǎn)會有功能失敗的借口。
  • 可檢驗:單元測試要明確地通過布爾值來表示檢測結(jié)果,而非通過其他諸如日志的輔助手段。
  • 及時性:要在開始編寫業(yè)務(wù)代碼前編寫,讓業(yè)務(wù)代碼去覆蓋測試。

最后

本文討論了單元測試的必要性以及單元測試中的一些重點注意實現(xiàn)。有人會覺得單元測試影響開發(fā)效率,但是站在項目管理者的角度上來說,有了單元測試項目才有了持續(xù)進(jìn)步的可能。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2021-08-04 10:51:04

數(shù)據(jù)庫單元測試SQL

2017-01-14 23:42:49

單元測試框架軟件測試

2022-06-30 09:20:32

單元測試測試

2014-04-24 13:43:37

CC++單元測試框架

2022-03-15 20:18:35

單元測試工具

2017-01-14 23:26:17

單元測試JUnit測試

2017-01-16 12:12:29

單元測試JUnit

2020-08-18 08:10:02

單元測試Java

2017-03-23 16:02:10

Mock技術(shù)單元測試

2023-07-26 08:58:45

Golang單元測試

2021-05-05 11:38:40

TestNGPowerMock單元測試

2011-07-04 18:16:42

單元測試

2020-05-07 17:30:49

開發(fā)iOS技術(shù)

2023-07-07 08:38:49

單元測試軟件開發(fā)

2011-05-16 16:52:09

單元測試徹底測試

2024-04-02 09:17:10

單元測試集成測試

2024-10-16 16:09:32

2011-04-18 13:20:40

單元測試軟件測試

2017-02-23 15:59:53

測試MockSetup

2009-09-25 10:33:25

Hibernate單元
點贊
收藏

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