phpunit單元測(cè)試(demo):https://github.com/qq1060656096/phpunit-test
百度經(jīng)驗(yàn)地址:http://jingyan.baidu.com/article/597a0643239f36312b524386.html
很多沒(méi)寫個(gè)單元測(cè)試的朋友,總覺(jué)得單元測(cè)試很難,還增加了工作了,或者把單元測(cè)試環(huán)境搭好了,也寫了很多單元測(cè)試,越寫越累,感覺(jué)代碼質(zhì)量沒(méi)提高,工作量反而提高很多。我們一起來(lái)學(xué)習(xí)下如何寫好單元測(cè)試。
我們分以下7個(gè)小點(diǎn)來(lái)講解:
1. ?為什么要些單元測(cè)試?
2. 單元測(cè)試與集成測(cè)試的區(qū)別?
3. 先些代碼還是先寫單元測(cè)試?
4. 誰(shuí)來(lái)編寫單元測(cè)試 ?
5. 如何避免無(wú)用的測(cè)試 ?
6. 測(cè)試代碼覆蓋率?
7. 單元測(cè)試中的"mock仿件"或者我們說(shuō)的打樁?
1. 為什么要些單元測(cè)試?
目的:
1. 提高軟件質(zhì)量
2. 減少bug
3. 減少重復(fù)的工作
4. 安全的重構(gòu)已有的代碼
5. 讓開(kāi)發(fā)者對(duì)程序穩(wěn)定性更有信心
重要性:
1. 運(yùn)行單元測(cè)試是為了保證代碼的行為和我們期望的結(jié)果一致。
2. 寫單元測(cè)試會(huì)增加代碼工作量,同時(shí)也節(jié)約了bug修復(fù)時(shí)間。
3. 如果沒(méi)有寫單元測(cè)試,沒(méi)有發(fā)現(xiàn)bug的情況下,程序在測(cè)試人員測(cè)試的時(shí)候才發(fā)現(xiàn)問(wèn)題或者在線上環(huán)境(正式環(huán)境)用戶使用才發(fā)現(xiàn)問(wèn)題,在去修復(fù)bug。開(kāi)發(fā)會(huì)花大量的精力去修復(fù)bug和走部署流程,測(cè)試也會(huì)花大量的時(shí)間去做了重復(fù)的測(cè)試。很不劃算。
4. 在線上某些場(chǎng)景下bug導(dǎo)致大量的數(shù)據(jù)丟失,需要花很大精力去修復(fù)數(shù)據(jù),或者根本沒(méi)辦修復(fù)數(shù)據(jù)導(dǎo)致嚴(yán)重的后果。
2. 單元測(cè)試與集成測(cè)試區(qū)別?
測(cè)試粒度不同:單元測(cè)試是程序最小的單元,而集成測(cè)試是一個(gè)功能,一組功能或者整個(gè)系統(tǒng)上
單元測(cè)試:程序的最小單元。
集成測(cè)試:也叫組裝測(cè)試或聯(lián)合測(cè)試,是在單元測(cè)試的基礎(chǔ)上,把所有模塊按系統(tǒng)設(shè)計(jì)要求組裝成功能或者系統(tǒng),實(shí)際中程序單元,測(cè)試通過(guò)了,不能保證程序組裝也能正常的工作,程序某些在局部反應(yīng)不出問(wèn)題,很有可能在全局或者特殊場(chǎng)景下暴露出問(wèn)題。
單元測(cè)試和集成測(cè)試很容易混為一談:因?yàn)閱卧獪y(cè)試和集成測(cè)試可以試用相同的工具和框架編寫。
3. 先寫代碼還是先寫單元測(cè)試?
編碼前,要先寫測(cè)試,很多沒(méi)有寫過(guò)單元測(cè)試的朋友會(huì)想,代碼都沒(méi)有,連測(cè)試的對(duì)象都沒(méi)有,我怎么寫單元測(cè)試?
1. 我們可以通過(guò)先話流程圖,寫偽代碼或者建模來(lái)解決這個(gè)問(wèn)題,這樣讓我們站在用戶的角色去開(kāi)發(fā),盡早的發(fā)現(xiàn)問(wèn)題
2. 避免我們開(kāi)發(fā)完了,某個(gè)功能模塊遺漏了的情況。
3. 這樣開(kāi)發(fā)出來(lái)的程序擴(kuò)展性、維護(hù)性很容易理解。
4. 誰(shuí)來(lái)編寫單元測(cè)試?
單元測(cè)試一般由開(kāi)發(fā)員自己些,但是我們自己對(duì)自己的代碼編寫單元測(cè)試的情況下,習(xí)慣性的往理想情況下編寫,開(kāi)發(fā)員最好不要針對(duì)自己的代碼編寫單元測(cè)試。應(yīng)該有其他開(kāi)發(fā)編寫,這樣減少了bug也提高了開(kāi)發(fā)的水平。
5. 如何避免無(wú)用的測(cè)試?
1. 只寫必要的測(cè)試
編寫自己覺(jué)得不靠譜的代碼,例如業(yè)務(wù)很復(fù)雜自己沒(méi)有吃透,以前沒(méi)有寫過(guò),感覺(jué)會(huì)產(chǎn)生無(wú)法預(yù)料的結(jié)果。
2. 只寫關(guān)鍵的測(cè)試
有時(shí)候必要的測(cè)試我們些不出來(lái),也沒(méi)有人知道,我們只能勉強(qiáng)跳過(guò)。但是關(guān)鍵性的測(cè)試不能跳過(guò),關(guān)鍵性測(cè)試就是:你寫的代碼的核心洛基。如果你不知道怎么處理,你知道要保證最終要的那條路線是可以走通的,將來(lái)重構(gòu)的時(shí)候,這條路線能確保你不會(huì)茫然。
3. 無(wú)用的測(cè)試
3.1 不要去測(cè)試開(kāi)發(fā)語(yǔ)言的標(biāo)準(zhǔn)庫(kù)和核心庫(kù),因?yàn)檫@些代碼都是久經(jīng)考驗(yàn)過(guò)得。雖然這些會(huì)出現(xiàn)小概率的錯(cuò)誤。(如果你確定是開(kāi)發(fā)語(yǔ)言的標(biāo)準(zhǔn)庫(kù)或者核心庫(kù)的問(wèn)題,你應(yīng)該測(cè)試標(biāo)準(zhǔn)庫(kù)和核心庫(kù),因?yàn)樗鼈兌紟в型暾臏y(cè)試用例)
3.2 不要去測(cè)試基礎(chǔ)框架和工具方法和外部依賴的有效性,當(dāng)你遇到這種問(wèn)題,你應(yīng)該打樁"mock"。
3.3 你只見(jiàn)過(guò)它測(cè)試通過(guò),沒(méi)有見(jiàn)過(guò)它測(cè)試失敗,可能這種測(cè)試從頭到尾都沒(méi)測(cè)試任何代碼,我們應(yīng)該手動(dòng)破壞代碼,以確保幀的覆蓋到了目標(biāo)代碼。
6. 測(cè)試代碼覆蓋率?
我們應(yīng)該忽略代碼覆蓋率:就算覆蓋率達(dá)到100%,和"靠譜"的代碼肯能有天壤之別,問(wèn)題就在于有些公司把代碼覆蓋率作為考核的標(biāo)準(zhǔn),這就讓開(kāi)發(fā)很容易就演變成"追求100%代碼覆蓋率",然后無(wú)所不用,連開(kāi)發(fā)都不懂,那就更悲劇了,一群人對(duì)著水分極大的代碼,然后對(duì)著"100%代碼覆蓋率"樂(lè)得合不弄嘴,想想都難受想哭。
測(cè)試中的仿件"mock"或者我們說(shuō)的打樁?
有時(shí)候?qū)Ρ粶y(cè)試的系統(tǒng)進(jìn)行測(cè)試很困難,因?yàn)樗蕾嚐o(wú)法在測(cè)試環(huán)境中使用的對(duì)象、組件、API或者它們不可用。在這種情況下,我們確保測(cè)試系統(tǒng)的內(nèi)部行為有更多的控制性和可見(jiàn)性,我們可以使用仿件"mock"或者打樁。
7. 什么情況下使用"仿件mock、樁件stubs" ?
1. 外部依賴不存在。
2. 外部依賴不會(huì)返回測(cè)試需要的結(jié)果,或者它有不良的副作用。
3. 如果外部依賴更變,會(huì)導(dǎo)致我們的測(cè)試失敗。
我們來(lái)看一個(gè)打樁示例:
1. 我們?cè)诰帉憜卧獪y(cè)試購(gòu)物車"Cart"類,依賴產(chǎn)品類"Product"和用戶類"User"。
2. 依賴產(chǎn)品類"Product"和用戶類"User"已經(jīng)測(cè)試過(guò)了。
3. 依賴的產(chǎn)品類"Product"和用戶類"User"是由他人開(kāi)發(fā)的。
示例問(wèn)題:
1. 產(chǎn)品類"Product"和用戶類"User"一旦出現(xiàn)問(wèn)題,不會(huì)讓我們誤以為購(gòu)物車類"Cart"出了問(wèn)題。
2. 不用為了創(chuàng)造很多前置條件,才能做出斷言。(如果這樣你應(yīng)該把它放到集成測(cè)試)。
3. 在測(cè)試購(gòu)物車時(shí),我們應(yīng)該避免使用"new Cart($userId, $productId, $quantity)"這種方式,這樣會(huì)出現(xiàn)程序中很多地方都去做了重復(fù)的查詢,并且影響程序的執(zhí)行效率,更不利于打樁,我們應(yīng)該使用這種方式"new Cart(User $user, Product $product, $quantity)"