單元測(cè)試,只是測(cè)試嗎?
推廣單元測(cè)試,僅僅達(dá)到單測(cè)覆蓋率是遠(yuǎn)遠(yuǎn)不夠的,我們還要學(xué)習(xí)寫(xiě)"易于測(cè)試"的代碼,以及"好"的測(cè)試,這樣才能讓單測(cè)真正發(fā)揮作用。本文將分享作者關(guān)于單元測(cè)試的思考與實(shí)踐。
首先我就來(lái)回答一下標(biāo)題提出的問(wèn)題:?jiǎn)卧獪y(cè)試除了是一種測(cè)試手段外,更是一種改善代碼設(shè)計(jì)的工具,容易寫(xiě)單測(cè)的代碼往往也具有更加良好的設(shè)計(jì)。
因而是任何自動(dòng)化測(cè)試工具都無(wú)法取代的。
當(dāng)然,這里也不是把自動(dòng)化測(cè)試工具給一棍子打死,自動(dòng)化測(cè)試工具也有自己的使用場(chǎng)景,比如測(cè)試遺留代碼,做長(zhǎng)鏈路測(cè)試等等。
這里需要強(qiáng)調(diào)一下 "工具" 屬性,工具能放大人的智力或者體力,讓干活的時(shí)候不會(huì)這么累,比如你去種樹(shù)帶把鏟子,你肯定不會(huì)把鏟子當(dāng)成負(fù)擔(dān)的,因?yàn)樗悄惴N樹(shù)的工具,你寫(xiě) Java,肯定不會(huì)因?yàn)? IDEA 啟動(dòng)時(shí)間長(zhǎng),就把它當(dāng)成一種負(fù)擔(dān),因?yàn)?IDEA 也是你寫(xiě) Java 的一個(gè)工具,很多人把寫(xiě)單測(cè)當(dāng)成一種負(fù)擔(dān),往往就是沒(méi)有意識(shí)到"單測(cè)"是一種工具,單純把他當(dāng)成一種測(cè)試。
一 品味篇
在品味篇,一起看看什么樣的代碼才是易于單測(cè)的。
Mock 工具的使用——毒藥還是解藥
你可能立刻就會(huì)產(chǎn)生和程序員小 A 類似的疑惑:"無(wú)論代碼寫(xiě)成什么樣,通過(guò) Mockito 和 PowerMock 肯定都是能寫(xiě)出單測(cè)來(lái)?所以通過(guò)單測(cè)真的改善代碼結(jié)構(gòu)嗎?"。
實(shí)際上,大量使用 Mock 工具的單測(cè)相當(dāng)于買(mǎi)櫝還珠,只具備測(cè)試的能力而無(wú)法幫助代碼設(shè)計(jì)。
商店系統(tǒng)案例
以一段非常簡(jiǎn)單的程序?yàn)槔?,假設(shè)這是一個(gè)商店系統(tǒng),里面有一個(gè)買(mǎi)面包的方法,里面會(huì)調(diào)用銀行提供的信用卡服務(wù) creditCardService 來(lái)扣除傳入的信用卡的錢(qián)。這段程序如果使用 Mockito 的話,估計(jì)你很快就能寫(xiě)出測(cè)試了,只需要把 creditCardService 給 Mock 掉,然后驗(yàn)證它傳入的參數(shù)就可以了。
如果總是像上面這樣思考的話,單測(cè)對(duì)于你改善代碼設(shè)計(jì)就沒(méi)什么幫助了。我們?cè)诮o代碼寫(xiě)單測(cè)的時(shí)候不應(yīng)該上來(lái)就思考用什么樣的工具來(lái)測(cè)試代碼,而是應(yīng)該思考如何重構(gòu)代碼,才能讓代碼變得更加容易測(cè)試。
還是上面這段代碼,我們換個(gè)角度,思考下如何重構(gòu)代碼,才能讓這段邏輯不需要 mock 就能測(cè)試?
返回一個(gè)執(zhí)行計(jì)劃,而不是立即執(zhí)行外部調(diào)用
上層拿到一個(gè) Payment 實(shí)體后,可以選擇立即執(zhí)行,或者稍后統(tǒng)一執(zhí)行
其實(shí)非常簡(jiǎn)單的一個(gè)辦法是,返回一個(gè)計(jì)劃,而不是立即就執(zhí)行外部調(diào)用,比如這里我們可以抽象出一個(gè) Payment 實(shí)體,表示從銀行卡里劃了多少錢(qián),外部拿到 Payment 實(shí)體后再?zèng)Q定是立即把錢(qián)劃掉,還是稍后把錢(qián)統(tǒng)一劃掉。此時(shí)這一段邏輯不需要 Mock 就可以測(cè)試了,只要校驗(yàn)方法返回的 Payment 對(duì)象里面的屬性是否正確即可。
到這里,你可能又有疑問(wèn)了,“費(fèi)了這么大事重構(gòu)代碼僅僅是為了好寫(xiě)單測(cè),值得嗎?”,如果你有這個(gè)疑惑的話,那你可能還是把單測(cè)僅僅當(dāng)成測(cè)試了,我之所以要把代碼重構(gòu)的好寫(xiě)單測(cè),是因?yàn)楹脤?xiě)單測(cè)的代碼還有其他諸多好處。
易于單測(cè)的代碼僅僅是易于單測(cè)嗎?
更多的性能優(yōu)化機(jī)會(huì)
就上面重構(gòu)的代碼為例吧,因?yàn)闃I(yè)務(wù)層返回的都是 Payment 對(duì)象,我可以這些 Payment 聚合起來(lái),最后統(tǒng)一執(zhí)行,比如下圖的這段代碼,我就可以把 Payment 按照銀行卡分組統(tǒng)一扣錢(qián),這樣就可以減少 rpc 調(diào)用的次數(shù),以后如果有需要的話,甚至可以直接將 Payment 作為消息發(fā)出去,到另一個(gè)系統(tǒng)執(zhí)行,業(yè)務(wù)層根本無(wú)需關(guān)心 Payment 最后是怎么執(zhí)行,只需要在付款的時(shí)候生成一個(gè) Payment 就可以了。
更加健壯的核心代碼
更加健壯的系統(tǒng)
另一個(gè)更大的好處是,好寫(xiě)單測(cè)的系統(tǒng)往往比不好寫(xiě)單測(cè)的系統(tǒng)更加健壯,如果一個(gè)系統(tǒng)大部分代碼都可以寫(xiě)無(wú) Mock 單測(cè),那么它看起來(lái)就像左圖一樣,外部調(diào)用只是薄薄的一層,可以隨意更換。
如果你的系統(tǒng)大部分代碼都一定要 Mock 才能測(cè)試的話,或者根本無(wú)法測(cè)試的話,就像右圖一樣,說(shuō)明你的業(yè)務(wù)根本就沒(méi)有自己的核心邏輯,而是和各種外部調(diào)用纏繞在一起。
另外需要說(shuō)明的是,圖中紅色的部分才是單測(cè)真正能夠起作用的場(chǎng)景,因?yàn)樗潜容^穩(wěn)定的業(yè)務(wù)邏輯,而且紅色部分的單測(cè)也比較好些,只需要傳幾個(gè)參數(shù)進(jìn)去,然后校驗(yàn)一下返回值就行了?;疑耐獠空{(diào)用部分理論上不寫(xiě)單測(cè)也無(wú)所謂,因?yàn)橥獠空{(diào)用是不穩(wěn)定的,即使你跟對(duì)方約定好了出入?yún)?shù),他依舊有可能返回不符合約定的參數(shù),或者直接就發(fā)生了網(wǎng)絡(luò)錯(cuò)誤,這一部分是集成測(cè)試發(fā)揮的場(chǎng)景。為什么在我們的系統(tǒng)里,大家都覺(jué)得單測(cè)沒(méi)用,其實(shí)我也覺(jué)得單測(cè)對(duì)我們現(xiàn)在的系統(tǒng)沒(méi)什么用,因?yàn)槲覀儸F(xiàn)在系統(tǒng)的主體代碼就像右圖一樣,大部分都是灰色的外部調(diào)用,單測(cè)能夠發(fā)揮作用的領(lǐng)域少之又少,即使寫(xiě)了覆蓋率 80% 的測(cè)試用例,又能測(cè)出來(lái)啥?
這里要再補(bǔ)充一下,我上面所說(shuō)的 “穩(wěn)定” 的含義,我說(shuō)紅色部分的“業(yè)務(wù)核心代碼”穩(wěn)定并不是說(shuō)業(yè)務(wù)一成不變,業(yè)務(wù)肯定是一直在變的,而是說(shuō)它的邏輯不會(huì)收到外部系統(tǒng)錯(cuò)誤的影響,不像灰色部分,外部系統(tǒng)一抖動(dòng)可能就會(huì)出問(wèn)題,因?yàn)榛疑糠植贿m合單測(cè)。
Mock 工具的定位
剛剛噴了這么久 Mock 工具,那 Mock 工具真正的定位究竟是什么呢?
- Mockito 是用來(lái)測(cè)試少量的不得不進(jìn)行外部調(diào)用的代碼。
- PowerMock 是用來(lái)測(cè)試設(shè)計(jì)得不好的遺留代碼的。
在 PowerMock 的文檔中已經(jīng)給出了警告,濫用它帶來(lái)的壞處或許比好處更多,所以當(dāng)我們寫(xiě)單測(cè)的時(shí)候不應(yīng)該上來(lái)就想著用這些 Mock 工具,而是應(yīng)該想想如何重構(gòu)代碼才能避開(kāi)這些工具的使用。
PowerMock 官方文檔的警告:
Putting it(PowerMock) in the hands of junior developers may cause more harm than good.
另外,我們?cè)倭牧膯螠y(cè)自動(dòng)化生成工具,我們剛好也有澄沨在做,無(wú)論是哪種單測(cè)生成工具,你會(huì)發(fā)現(xiàn)工具生成的單測(cè)到處都是 Mockito 和 PowerMock,顯然不符合單測(cè)的定位,但是這種工具也是有意義的,當(dāng)系統(tǒng)里到處都是不好寫(xiě)單測(cè)的遺留代碼時(shí),用這個(gè)工具生成一下也能幫助我們覆蓋一小部分測(cè)試,對(duì)于我們系統(tǒng)目前的情況還是很有必要的。
再來(lái)一個(gè)重構(gòu)的例子
寫(xiě)有外部調(diào)用的靜態(tài)方法:
最后的結(jié)果:
為了加深大家印象,這里再舉個(gè)一個(gè)例子。比如下面這個(gè)方法,我在靜態(tài)方法中調(diào)用先通過(guò)對(duì) Business 的對(duì)象的各種處理,拿到了 rpc 調(diào)用的地址和版本號(hào),然后使用這個(gè)地址和版本號(hào)加載一個(gè)初始化好的 hsf(阿里內(nèi)部使用的 rpc 框架)泛化調(diào)用對(duì)象返回,這個(gè)方法的單測(cè)顯然十分難寫(xiě),因?yàn)? init 會(huì)發(fā)生網(wǎng)絡(luò)調(diào)用,導(dǎo)致測(cè)試失敗。這個(gè)時(shí)候我們要反思一下單測(cè)不好寫(xiě)的原因,是因?yàn)槲覀冞`背了一條編碼的基本原則——“不能在靜態(tài)方法中寫(xiě)外部調(diào)用”,如果你就是想在靜態(tài)方法中進(jìn)行外部調(diào)用,那應(yīng)該怎么辦呢?還是像之前的例子一樣,返回一個(gè)計(jì)劃,讓外部調(diào)用,首先保持代碼無(wú)副作用的部分不動(dòng),這一部分本來(lái)就沒(méi)有外部調(diào)用,放在靜態(tài)方法里執(zhí)行也什么事情,然后把外部調(diào)用部分封到一個(gè) Operator 里面(比如這里就是 RpcLoader)返回給上一層,上一層自己選擇立即調(diào)用還是稍后調(diào)用。
這么做除了好寫(xiě)單測(cè),還有什么好處呢?最顯而易見(jiàn)的一點(diǎn)就是代碼變得可復(fù)用了,更重要的一點(diǎn)是防腐,你會(huì)發(fā)現(xiàn) hsf 影響范圍被局限在 RpcLoader 里面,以前哪怕它的 API 出現(xiàn)什么變化,或者要換別的框架,都是件非常容易的事情。
為什么單測(cè)能夠驗(yàn)證代碼結(jié)構(gòu)的合理性
前面我提到的這些關(guān)于代碼結(jié)構(gòu)的概念聽(tīng)起來(lái)是不是非常耳熟,在別的領(lǐng)域也經(jīng)常聽(tīng)到,比如面向?qū)ο笾械?ldquo;高內(nèi)聚,低耦合”,DDD 中所提到的“核心域”,“防腐層”,函數(shù)式編程所倡導(dǎo)的“隔離副作用”,你會(huì)發(fā)現(xiàn),好的編程范式倡導(dǎo)的東西都是類似的。
上面這三種評(píng)價(jià)代碼的方式其實(shí)都是比較“主觀”的,什么樣的代碼才能叫“高內(nèi)聚”,在每個(gè)人看來(lái)可能都不一樣。但是對(duì)于是否易于寫(xiě)單測(cè),大家的標(biāo)準(zhǔn)基本是一樣的,難寫(xiě)單測(cè)的系統(tǒng)給誰(shuí)都很難寫(xiě)。而好寫(xiě)單測(cè)的代碼一般都滿足編程范式所倡導(dǎo)的原則,所以寫(xiě)單測(cè)的難易程度可以作為一個(gè)非??陀^的代碼質(zhì)量評(píng)價(jià)指標(biāo)。
如果有人跟你說(shuō)他這段代碼設(shè)計(jì)得非常好,但是就是不好寫(xiě)單元測(cè)試,千萬(wàn)不要相信他。
另外再提一下設(shè)計(jì)模式,如果只是照著書(shū)抄抄代碼,設(shè)計(jì)模式是非常簡(jiǎn)單的,關(guān)鍵是要用對(duì)場(chǎng)景,一不小心就會(huì)只學(xué)到了“形”,而沒(méi)有學(xué)到“神”,“形神兼?zhèn)?rdquo;的設(shè)計(jì)模式往往會(huì)讓代碼變得更加容易測(cè)試,如果用了設(shè)計(jì)模式發(fā)現(xiàn)系統(tǒng)變得更難測(cè)試了,那設(shè)計(jì)模式十有八九用得不對(duì)。
如果有個(gè)程序員跟你說(shuō)我程序的性能達(dá)到了多少 QPS,你肯定會(huì)立馬拿起測(cè)試工具就去測(cè),看到底能不能到達(dá)這個(gè) QPS。但是如果有程序員畫(huà)了框框圖說(shuō)他的代碼分成了 A B C 模塊,要怎么驗(yàn)證他的代碼真的分成了這幾個(gè)模塊呢?很簡(jiǎn)單,你看看每一個(gè)模塊能否脫離其他模塊單獨(dú)測(cè)試就可以了,如果單獨(dú)測(cè)試非常困難,那就說(shuō)明模塊并沒(méi)有真的分開(kāi),而是或多或少耦合在了一起。
易于單測(cè)的等級(jí)
現(xiàn)在我們可以總結(jié)易于單測(cè)的幾等級(jí)了。和別的領(lǐng)域不太一樣,別的領(lǐng)域你高級(jí)的工具用得越多,可能越厲害,但是在單測(cè)這個(gè)領(lǐng)域,使用越多的高級(jí)工具,反而是更加糟糕的測(cè)試。
另外,對(duì)這些規(guī)則也不要死腦筋,這些只適合業(yè)務(wù)含義比較豐富的代碼,如果你就是在寫(xiě)一些封裝外部調(diào)用的代碼,這部分代碼我覺(jué)得不寫(xiě)單測(cè)也是可行的。
- 第一級(jí),易于單測(cè):大部分代碼不需要 Mock 就可以測(cè)試,少量的外部調(diào)用代碼需要 Mockito。
- 第二級(jí),能夠單測(cè):超過(guò)一半的代碼需要 Mock 才能測(cè)試,但是這些測(cè)試也不是特別難寫(xiě)。
- 第三級(jí),難以單測(cè):大量 Mock,甚至大量使用了 PowerMock。
- 第四級(jí),無(wú)法單測(cè):模塊被設(shè)計(jì)的及其復(fù)雜,連開(kāi)發(fā)者自己都無(wú)法理解,更無(wú)法寫(xiě)單測(cè)。
二 實(shí)踐篇
在上一篇學(xué)習(xí)了關(guān)于單測(cè)的正確觀念后,這一篇再來(lái)聊一聊關(guān)于單測(cè)的最佳實(shí)踐。
單元測(cè)試的運(yùn)行速度重要嗎?
很多人會(huì)覺(jué)得單測(cè)反正也不是系統(tǒng)中的代碼,運(yùn)行的快慢無(wú)所謂,然后寫(xiě)出很多其慢無(wú)比的單測(cè),以至于系統(tǒng)全量跑一次單測(cè)要幾十分鐘。這樣的話就完全偏離了單測(cè)的定位,單測(cè)的目的就是為了方便快速迭代,改了兩行代碼就可以在本地用 30 秒到幾分鐘的時(shí)間全量跑一次單測(cè)來(lái)確定影響范圍,而不是每次都要通讀系統(tǒng)源碼才能知道改動(dòng)的影響范圍,這樣新人很快就可以大膽改代碼了,而不是先花幾個(gè)月通讀系統(tǒng)源碼,或者先踩好幾個(gè)坑,才能上手干活。那些全量跑單測(cè)要幾十分鐘的系統(tǒng),他的開(kāi)發(fā)者根本就不會(huì)在本地全量運(yùn)行單測(cè),每次都在 aone 上跑半天才知道單測(cè)不過(guò),這樣的單測(cè)就形同虛設(shè)了。
違背這個(gè)原則的典型反例,就是在單測(cè)中啟動(dòng) Spring。
數(shù)據(jù)驅(qū)動(dòng)測(cè)試(Data Driven Test)
不好的單元測(cè)試常常只用一組正常測(cè)試數(shù)據(jù)進(jìn)行測(cè)試,實(shí)際上我們應(yīng)該使用多組數(shù)據(jù),包括正常和異常數(shù)據(jù),輸入模塊,看返回值是否符合預(yù)期。使用多組測(cè)試數(shù)據(jù)是否就意味著多寫(xiě)很多代碼呢?并不是,我們只要注意將測(cè)試用例的邏輯與數(shù)據(jù)分離就可以,測(cè)試代碼依次讀取測(cè)試數(shù)據(jù),校驗(yàn)其是否符合預(yù)期。這樣的邏輯與數(shù)據(jù)分離的測(cè)試一般稱做 “數(shù)據(jù)驅(qū)動(dòng)測(cè)試”,常見(jiàn)的單元測(cè)試框架都會(huì)提供這種支持。
"數(shù)據(jù)驅(qū)動(dòng)測(cè)試" 的概念還是太抽象了,這里我們看兩段代碼,左圖未分離數(shù)據(jù)與用例,右圖則做了分離,能夠看出很明顯的不同,右圖是基于 Spock 單元測(cè)試框架來(lái)寫(xiě)的,不熟悉的人看上去可能比較奇怪,可以把 where 標(biāo)簽下的代碼看成一張表格,每一行都是一組測(cè)試數(shù)據(jù),Spock 框架會(huì)將其依次代入 testAdd 方法參數(shù)進(jìn)行測(cè)試。
測(cè)試數(shù)據(jù)未與用例分離
測(cè)試數(shù)據(jù)與用例分離
大家所熟悉的 junit 框架也是可以做的,但是需要寫(xiě)一個(gè)額外的內(nèi)部類,加上@RunWith(Parameterized.class),寫(xiě)一個(gè) data 靜態(tài)方法,然后返回需要測(cè)試的數(shù)據(jù)組,然后 junit 就會(huì)依次將數(shù)據(jù)填入這個(gè)類的屬性中,運(yùn)行這個(gè)類中的全部測(cè)試用例。
基于 junit 的數(shù)據(jù)驅(qū)動(dòng)測(cè)試
基于 Spock 的數(shù)據(jù)驅(qū)動(dòng)測(cè)試
如何測(cè)試私有方法
大家寫(xiě)單測(cè)時(shí)常有的一個(gè)困惑就是私有方法怎么測(cè)試?雖然理論上私有方法不需要寫(xiě)單測(cè),但是有些私有方法邏輯比較復(fù)雜,還是值得單獨(dú)寫(xiě)測(cè)試的,目前公認(rèn)比較好的實(shí)踐就是將修飾符從 private 改成 protected, 這也是很多開(kāi)源項(xiàng)目給單測(cè)留口子的方法。如果你的項(xiàng)目剛好有引入 guava 的話,可以再給方法加上一個(gè) @VisibleForTesting 的注解,表示僅僅是出于單元測(cè)試需要修改的修飾符。
一個(gè)典型的例子:
三 TDD 與 BDD
最后一篇來(lái)講一兩個(gè)大家可能經(jīng)常聽(tīng)說(shuō)過(guò)的理念,TDD 和 BDD。個(gè)人覺(jué)得這兩個(gè)理念都比較極端,實(shí)際中很難應(yīng)用,啟發(fā)意義大于其實(shí)用意義,所以放在最后,希望能帶來(lái)一些啟發(fā)。
TDD
TDD 強(qiáng)調(diào)讓寫(xiě)代碼的過(guò)程形成一個(gè)循環(huán),第一步是為你要做的功能寫(xiě)一個(gè)單元測(cè)試,跑一下發(fā)現(xiàn)沒(méi)有通過(guò)(畢竟你還沒(méi)有實(shí)現(xiàn)代碼),即圖中的 TEST FAILS,俗稱“紅燈”,之后編寫(xiě)能夠通過(guò)全部測(cè)試的“最小代碼”,之所以強(qiáng)調(diào)“最小代碼”,就是為了防止過(guò)度優(yōu)化,現(xiàn)實(shí)中我們經(jīng)常會(huì)因?yàn)榇a過(guò)度優(yōu)化,或者過(guò)度設(shè)計(jì),導(dǎo)致很多遺留問(wèn)題,在這個(gè)階段,只管用最快最臟的代碼實(shí)現(xiàn)就好了,不用管太多設(shè)計(jì)問(wèn)題。這個(gè)階段俗稱“綠燈”。
最重要的就是下面的“重構(gòu)”(REFACTOR)階段了,前面的代碼雖然可能很臟,但是至少是正確,也有足夠的測(cè)試來(lái)保障邏輯的正確,這個(gè)時(shí)候就可以大刀闊斧地重構(gòu)代碼了,保證代碼繼續(xù)保持最優(yōu)。
這啟發(fā)我們兩點(diǎn):
- 單測(cè)必須能夠快速運(yùn)行,因?yàn)閱螠y(cè)是經(jīng)常要在本地全量運(yùn)行的,只有運(yùn)行足夠快,才能在 TDD 的循環(huán)中快速迭代。
- 好的代碼并不是一次性就設(shè)計(jì)出來(lái)的,而是持續(xù)重構(gòu)出來(lái),而單測(cè)是持續(xù)重構(gòu)的前提。
BDD
我常常抱怨產(chǎn)品經(jīng)理在提需求時(shí)沒(méi)有想清楚,比如下圖,如果讓產(chǎn)品經(jīng)理也可以寫(xiě)出可執(zhí)行的測(cè)試用例的話,情況想必會(huì)好很多。BDD 就是這么一個(gè)想法。

產(chǎn)品經(jīng)理提需求
不知道大家有沒(méi)有在有的項(xiàng)目里見(jiàn)過(guò) .story 文件,它本質(zhì)上就是一種集成測(cè)試腳本,只不過(guò)是用自然語(yǔ)言描述,它包含敘述,場(chǎng)景和步驟三部分,比如上圖就是一個(gè)書(shū)店管理應(yīng)用的 .story 文件,文件中敘述(Narrative) 和 場(chǎng)景(Scenario) 只是幫助思考的,本身并包含在測(cè)試用例的邏輯中,測(cè)試用例主要由 Given, When 和 Then 開(kāi)頭的語(yǔ)句組成,含義如下:
story 文件示例
story 文件自己當(dāng)然是無(wú)法執(zhí)行的,需要框架提供支持,JBehave 就是這么一種框架(右圖),能夠定義各種 Given,When,Then 語(yǔ)句的實(shí)現(xiàn),下圖的代碼本質(zhì)上就是個(gè)基于 Selenide 的自動(dòng)化界面點(diǎn)擊測(cè)試,它支撐 story 文件的執(zhí)行。我們以這個(gè) story 文件為依據(jù),就可以像 TDD 循環(huán)一樣,先測(cè)試不通過(guò)(紅燈),然后用最小的代碼讓測(cè)試通過(guò)(綠燈),最后重構(gòu)代碼。只不過(guò)這個(gè)循環(huán)可能會(huì)耗時(shí)好好幾天,乃至幾個(gè)星期。而 TDD 一個(gè)循環(huán)可能只需要幾個(gè)小時(shí),所以說(shuō) BDD 是集成測(cè)試版的 TDD。
JBehave 框架
敏捷
我們往往會(huì)覺(jué)得 TDD 和 BDD 會(huì)嚴(yán)重拖慢迭代速度,值得諷刺的是,TDD 和 BDD 恰恰是敏捷開(kāi)發(fā)實(shí)踐的重要組成部分:
圖源維基百科 Agile software development
我們學(xué)習(xí)敏捷開(kāi)發(fā)的時(shí)候,常常只學(xué)習(xí)到它的 “快”,而忽略了敏捷開(kāi)發(fā)所提出的質(zhì)量保證方法。敏捷開(kāi)發(fā)所謂的“快”,是指在代碼質(zhì)量充分保證下的“快”,而不是做完功能就直接上線。
四 如何學(xué)習(xí)寫(xiě)單測(cè)
學(xué)習(xí)單測(cè)的關(guān)鍵還是多實(shí)踐,多看看別人好的單測(cè)怎么寫(xiě)。比如可以給一些公認(rèn)代碼優(yōu)秀的開(kāi)源項(xiàng)目提交代碼。
五 總結(jié)
- 單測(cè)能夠幫助我們驗(yàn)證代碼設(shè)計(jì)的合理性。
- 含有核心業(yè)務(wù)的代碼應(yīng)該首先思考如何讓主體業(yè)務(wù)邏輯可以寫(xiě)無(wú) Mock 單測(cè)。
- 用例數(shù)據(jù)盡量和測(cè)試邏輯分離。
參考資料
參考資料
[1]Test-Driven Java Development
https://www.oreilly.com/library/view/test-driven-java-development/9781783987429/
[2]Wiki Agile software development
https://en.wikipedia.org/wiki/Agile_software_development
[3]PowerMock
https://powermock.github.io/
[4]JBehave
https://jbehave.org/
[5]Spock
http://spockframework.org/
[6]JUnit
https://junit.org/junit4/
[7]Learning to Love TDD
https://medium.com/swlh/learning-to-love-tdd-f8eb60739a69
【本文為51CTO專欄作者“阿里巴巴官方技術(shù)”原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)聯(lián)系原作者】