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