前言
寫單元測試常碰到的兩種問題:
- dependency 的問題,導(dǎo)致無法隔離相依,無法模擬或驗證相依對象的互動
- 要模擬的相依對象太多
其實,這都是散發(fā)著壞味道的象徵胃惜。
本文
當(dāng)在撰寫 isolated unit test 時發(fā)現(xiàn),要 stub 的對象太多時哪雕,往往是兩種 bad smell 的徵兆船殉。
- 單一職責(zé)的另一種 anti-pattern, 把一個職責(zé)過分拆細,導(dǎo)致要完成某一件事得組合太多零碎的相依對象才能運作斯嚎。(比較常見)
- 測試目標對象 (SUT) 的職責(zé)過大利虫,需要很多相依對象互動才能完成自己的職責(zé)挨厚。(機率較小)
另外要留意的一個要點糠惫,如果假對象都是 mock, 而非 stub, 那通常代表用錯了疫剃。
mock 是透過注入假對象,用來驗證測試目標與這個相依假對象之間的互動硼讽,也就是 assertion 的目的巢价。而 stub 對象是用來模擬相依對象的動作,以便讓單元測試能獨立驗證測試目標對象本身的邏輯是否正確固阁。
因為單元測試一次只測一件事壤躲,所以請勿同時對測試目標對象進行 assert, 又對 mock 對象進行 assert。因為這兩個 assert 的關(guān)注點备燃,勢必是不同件事碉克。
在《單元測試的藝術(shù)》一書上提到,mock 跟 stub 在實務(wù)上的比例大約是 5%:95%(個人經(jīng)驗差不多是 10%:90%)并齐。過多過細的 mock, 會讓 test cases 的穩(wěn)定度 (Robustness) 下降, 孩子生了就得養(yǎng)漏麦,所以需要生的時候再生就好。
剛好的 mock, 可以有效降低測試用例幾倍的維護成本冀膝。
如果你非得驗證到 mock 對象互動所接收到的參數(shù)唁奢,請記得「只驗證意義,例如透過 regex pattern窝剖。而不是驗證參數(shù)的所有細節(jié)」
補充
測試用例的撰寫與維護,往往是 production code 設(shè)計品質(zhì)的照妖鏡酥夭。如果你碰到以下的狀況赐纱,代表:
- 一個需求異動,要加好多的測試用例:代表違反單一職責(zé)與開放封閉原則熬北,應(yīng)該只需要新增 class 做切換疙描,而不是對原本內(nèi)容修改。
- 一個需求異動讶隐,要改好多測試用例:代表違反單一職責(zé)起胰,一個對象的職責(zé)太大。
- 要 stub 的對象很多:代表違反單一職責(zé)巫延,因為同一個職責(zé)被拆到太多對象身上效五,要做一件事就需要很多個對象一起,才能正常運作一件事炉峰。
- 互動的方式一改畏妖、參數(shù)一改,測試用例就壞:代表 mock 太深疼阔,mock 可以只驗證互動次數(shù)戒劫、參數(shù)的意義以及參數(shù)的完整內(nèi)容半夷,越後面代表綁得越深。
- UI 一改迅细,所有測試用例都要跟著修改:layout 與 scenario 耦合性太高巫橄,請透過 page object pattern 從測試程式設(shè)計上解耦。