什么是單元測(cè)試?為什么要做?
什么是UT?
UT(Unit Test)即單元測(cè)試
UT有什么價(jià)值?
大部分的開(kāi)發(fā)都不喜歡寫UT,原因無(wú)非以下幾點(diǎn):
產(chǎn)品經(jīng)理天天催進(jìn)度,哪有時(shí)間寫UT UT是測(cè)試自己的代碼,自測(cè)?那要QA何用? 自測(cè)能測(cè)出bug?都是基于自身思維,就像考試做完第一遍,第二遍檢查一樣,基本檢查不出什么東西 UT維護(hù)成本太高,投入產(chǎn)出比太低 不會(huì)寫UT
本人實(shí)習(xí)的時(shí)候做測(cè)試的,那時(shí)候知道一個(gè)測(cè)試模型。如下圖:
圖的意思就是越底層做的測(cè)試效果越好,越往上則越差。也就是說(shuō)大部分公司現(xiàn)在做的功能測(cè)試其實(shí)是效果最差的一種測(cè)試方式。另外,QA界有個(gè)現(xiàn)場(chǎng):大家都知道功能測(cè)試沒(méi)技術(shù)含量,那如何使自己突出呢?答案就是:自動(dòng)化測(cè)試。現(xiàn)實(shí)是沒(méi)幾個(gè)公司能做好自動(dòng)化測(cè)試, 業(yè)界做的比較好的百度算一個(gè)。
那么為啥自動(dòng)化測(cè)試這么難做的?在這個(gè)模型當(dāng)中,越往上黑盒越大,自動(dòng)化測(cè)試難度就越大。這句話反過(guò)來(lái)就是越往下自動(dòng)化測(cè)試就越好做?沒(méi)錯(cuò),UT其實(shí)是最容易實(shí)現(xiàn)且效果最好的自動(dòng)化測(cè)試。所以在很多公司出現(xiàn)一種現(xiàn)場(chǎng):QA寫UT。
原因總結(jié)一下就兩點(diǎn):開(kāi)發(fā)不愿意寫UT,QA想自動(dòng)化測(cè)試解放自己。以上的模型只是理論上說(shuō)明UT具有巨大的價(jià)值,但是真的如此么?我只想說(shuō),只有真正嘗到UT的好處的甜頭才會(huì)意識(shí)到UT的價(jià)值。
Unit Test & Intergration Test
單元測(cè)試和集成測(cè)試的界線我相信大部分開(kāi)發(fā)也是不清晰的。個(gè)人理解單元測(cè)試針對(duì)于一塊業(yè)務(wù)邏輯最小的單元,太抽象。物理上可以簡(jiǎn)單理解為一個(gè)類的方法, 可以是public方法也可以是private方法。一個(gè)單元測(cè)試不應(yīng)該包含外部依賴的邏輯,反之就是集成測(cè)試了。
問(wèn)題的核心就在于此。
一個(gè)service的一個(gè)接口實(shí)現(xiàn)可能依賴很多第三方:
1.本地其它的service
2.dao調(diào)用
3.rpc調(diào)用
4.微服務(wù)調(diào)用。
如下圖:
也就是說(shuō)你的單元測(cè)試,真正調(diào)用了外部依賴那就是集成測(cè)試。這其實(shí)很常見(jiàn)對(duì)不?我們先說(shuō)這種情況下如何集成測(cè)試。
Local Integration Test
第一步:數(shù)據(jù)準(zhǔn)備
第二步:執(zhí)行邏輯最簡(jiǎn)單,就是調(diào)用一下我們測(cè)試的方法即可
第三步:驗(yàn)證
集成測(cè)試一般是調(diào)用service,或者dao的接口驗(yàn)證。
舉個(gè)例子:CRUD操作的集成測(cè)試
Remote Integration Test
假設(shè)我們一個(gè)service實(shí)現(xiàn)依賴某個(gè)RPC Service
第一步:數(shù)據(jù)準(zhǔn)備
跑到別人家的數(shù)據(jù)庫(kù)插幾條數(shù)據(jù)?或者跟PRC Service的Owner商量好,搭一個(gè)測(cè)試環(huán)境供我們測(cè)試?有些公司還真有專門的自動(dòng)化測(cè)試環(huán)境,那么即使有測(cè)試環(huán)境,那如何實(shí)現(xiàn)各種case場(chǎng)景下,第三方Service很配合的返回?cái)?shù)據(jù)給我們?想想都蛋疼。
第二步:執(zhí)行方法
假設(shè)我們成功的解決了第一步中的問(wèn)題,皆大歡喜。現(xiàn)在來(lái)看第二步,假設(shè)我們的service里面調(diào)用了另一個(gè)RPC Service創(chuàng)建了很多數(shù)據(jù),跑了無(wú)數(shù)次case,結(jié)果....RPC Service對(duì)應(yīng)的數(shù)據(jù)庫(kù)都是我們的臟數(shù)據(jù),如何清理?而且他們敢隨便刪數(shù)據(jù)嗎?想想也蛋疼。
第三步:輸出驗(yàn)證
假設(shè)我們又愉快的解決了第二步中的問(wèn)題。現(xiàn)在來(lái)看第三步,假設(shè)我們的方法執(zhí)行最終輸出是創(chuàng)建了一個(gè)訂單,訂單當(dāng)然是調(diào)用訂單Service接口了,那么我們?nèi)绾悟?yàn)證訂單是否成功創(chuàng)建了呢?
或許可以調(diào)用訂單Service查詢訂單的接口來(lái)驗(yàn)證。很明顯大多數(shù)情況下并沒(méi)有這么完美。想想也蛋疼呀。通過(guò)以上分析,Local Integration Test是可行的,Remote Integration Test基本不可行。
那么有沒(méi)有什么辦法解決呢?答案就是Mock
第一步:Mock RPC Service 想返回什么數(shù)據(jù)就返回什么數(shù)據(jù) 第二步:還是Mock接口,想調(diào)用幾次就調(diào)用幾次 第三步:這一步等到下面講完單元測(cè)試就明白了
Unit Test
上面我們談到Mock可以解決外部依賴的問(wèn)題,現(xiàn)在有很多Mock的開(kāi)源框架比如:mockito。那么問(wèn)題來(lái)了,既然我們可以mock第三方遠(yuǎn)程依賴,為何不mock dao、local service呢?
沒(méi)錯(cuò)外部依賴全部mock掉,就是單元測(cè)試了。因?yàn)槲覀冎魂P(guān)心所測(cè)試的方法的業(yè)務(wù)邏輯,也就是真正高內(nèi)聚的邏輯單元了。如下圖:
好處如下:
沒(méi)有什么數(shù)據(jù)是造不出來(lái)的,通通返回Mock的對(duì)象 代碼中的異常處理代碼,也可以通過(guò)mock接口,使之拋出異常 不產(chǎn)生任何臟數(shù)據(jù) 跑case更快了,因?yàn)椴挥脝?dòng)整個(gè)項(xiàng)目,相當(dāng)于Main方法
有人會(huì)說(shuō),都mock了還測(cè)試個(gè)蛋蛋。
這就是對(duì)于單元測(cè)試的理解了,單元測(cè)試應(yīng)該只針對(duì)于目標(biāo)方法的業(yè)務(wù)邏輯測(cè)試,dao、其它service應(yīng)該在它們自身的單元測(cè)試去測(cè)試。對(duì)于依賴的第三方,我們應(yīng)該信任它們能正確的完成我們所預(yù)期的。這句話很難理解對(duì)不對(duì)?
舉幾個(gè)例子
例子一:方法的最后是執(zhí)行dao的create操作,那么該如何驗(yàn)證?
我們應(yīng)該驗(yàn)證的內(nèi)容是:
dao的create方法被調(diào)用了 調(diào)用次數(shù)是對(duì)的 調(diào)用參數(shù)也是對(duì)的
沒(méi)錯(cuò),只要這三個(gè)驗(yàn)證通過(guò),那么這個(gè)case執(zhí)行就是通過(guò)的。因?yàn)槲覀兿嘈興ao的create操作能正確的完成我們所預(yù)期的,只要我們調(diào)用了正確的次數(shù)并且參數(shù)都是對(duì)的。
dao的執(zhí)行的正確性保證是在該dao的單元測(cè)試做的。在Remote Integration Test里面第三步驗(yàn)證道理是一樣的,我們應(yīng)該驗(yàn)證RPC接口被調(diào)用了且次數(shù)和參數(shù)都是對(duì)的,那么我們的case就算通過(guò)了,至于,RPC服務(wù)端是否正確執(zhí)行是它們的事情不是我們所關(guān)心的。另外,搜索公眾號(hào)互聯(lián)網(wǎng)架構(gòu)師后臺(tái)回復(fù)“2T”,獲取一份驚喜禮包。Mockito框架的verify接口就是做這件事情的。如果你理解了上述內(nèi)容,那么你就開(kāi)竅了,UT不在變得這么難寫。
什么時(shí)候用單元測(cè)試,什么時(shí)候用集成測(cè)試?
在本人的實(shí)踐中摸索發(fā)現(xiàn),對(duì)于簡(jiǎn)單的業(yè)務(wù),比如crud型的瘦service,比較適合于集成測(cè)試。以下情況適合于單元測(cè)試:
case細(xì)到什么程度為好?
這個(gè)問(wèn)題也是比較經(jīng)典的,一個(gè)方法要是所有的路徑都覆蓋到,那么要寫很多的case,說(shuō)真的累死人。
我的建議是兩個(gè)原則:
1、核心邏輯,容易出錯(cuò)的邏輯一定要覆蓋到
2、根據(jù)自己的時(shí)間。沒(méi)必要寫的非常多,畢竟case維護(hù)成本很高,業(yè)務(wù)邏輯一改,case得跟著改。
總結(jié)
本人目前在從事于開(kāi)源項(xiàng)目(Apollo(配置中心) )研發(fā),開(kāi)源項(xiàng)目對(duì)代碼質(zhì)量要求相對(duì)來(lái)說(shuō)高一些,UT當(dāng)然是很重要的一環(huán)。剛開(kāi)始也不會(huì)寫UT,當(dāng)然態(tài)度上也不重視UT。
老大的代碼UT覆蓋率很高,抱著對(duì)開(kāi)源負(fù)責(zé)的態(tài)度慢慢接受學(xué)習(xí)UT,到后來(lái)嘗了幾次甜頭后,發(fā)現(xiàn)UT真的很實(shí)用,價(jià)值也很高,但是很遺憾UT被大部分開(kāi)發(fā)所忽略。當(dāng)然本人對(duì)UT的理解、實(shí)踐還不夠,仍需繼續(xù)實(shí)踐模式。
最后說(shuō)一句:當(dāng)開(kāi)發(fā)完功能,跑完UT,你可以放心的上線了的時(shí)候,你的UT就成功了。
正文結(jié)束
1.救救大齡碼農(nóng)!45歲程序員在國(guó)務(wù)院網(wǎng)站求助總理!央媒網(wǎng)評(píng)來(lái)了...
3.從零開(kāi)始搭建創(chuàng)業(yè)公司后臺(tái)技術(shù)棧
5.37歲程序員被裁,120天沒(méi)找到工作,無(wú)奈去小公司,結(jié)果懵了...
6.IntelliJ IDEA 2019.3 首個(gè)最新訪問(wèn)版本發(fā)布,新特性搶先看

