如何設(shè)計(jì)單元測試?
單元測試設(shè)計(jì)方法
單元測試用例,和普通測試用例的設(shè)計(jì),沒有太多不同,常見的就是等價(jià)類劃分、邊界值分析等。而測試用例的設(shè)計(jì)其實(shí)也是開發(fā)者應(yīng)該掌握的基本技能。
等價(jià)類劃分
把所有輸入劃分為若干分類,從每個(gè)分類中選取少數(shù)有代表性的數(shù)據(jù)做為測試用例。
例如,一個(gè)方法計(jì)算輸入?yún)?shù)的絕對(duì)值的倒數(shù),如果是輸入是 0,則拋異常。那么對(duì)這個(gè)方法寫測試的話,就應(yīng)該有三個(gè)等價(jià)類,輸入是負(fù)數(shù)、0 以及正數(shù)。所以我可以選取一個(gè)負(fù)數(shù)、一個(gè)正數(shù)以及 0 來設(shè)計(jì)三個(gè)測試用例。
再舉個(gè)例子,某個(gè)方法是根據(jù)醫(yī)生的認(rèn)證狀態(tài),發(fā)送不同的消息。那么等價(jià)類可能有三種,未認(rèn)證、普通認(rèn)證但無權(quán)威認(rèn)證、普通認(rèn)證且權(quán)威認(rèn)證,某些情況下可能還會(huì)包括無普通認(rèn)證但有威認(rèn)證。
邊界值分析
邊界值是指劃分等價(jià)類后,在邊界附近的一些輸入數(shù)據(jù),這些輸入往往是最容易出錯(cuò)的。
例如,對(duì)于上面計(jì)算絕對(duì)值的倒數(shù)的例子,那么邊界值就包括 Integer.min、-1、0、1、Integer.max 等。再舉個(gè)例子,文本框輸入范圍為 1 - 255 個(gè)字符,那么等價(jià)類按輸入長度劃分有三類 0、1 到 255、大于 255,而邊界值則包括 0、1、2、254、255、256 等。
其他類似于空數(shù)組、數(shù)組的第一個(gè)和最后一個(gè)、報(bào)表的第一行和最后一行等等,也是屬于邊界值,需要特別關(guān)注。
其他方法
除了上面提到的幾種,測試設(shè)計(jì)方法還有幾種常用的:
場景法。場景法是根據(jù)模塊實(shí)際使用的場景,例如 API 的實(shí)際調(diào)用方法、系統(tǒng)的實(shí)際需求場景和處理邏輯創(chuàng)建的用例。這種方法比較直觀,并且用例貼近實(shí)際需求的,不可忽視。
錯(cuò)誤推測。錯(cuò)誤推測其實(shí)就是憑直覺,考慮最容易出錯(cuò)的情況來設(shè)計(jì)用例。例如,我們直到新用戶、重復(fù)請(qǐng)求、并發(fā)、弱網(wǎng)、大數(shù)據(jù)量等情況都是非常容易出錯(cuò)的,那么可以針對(duì)性的設(shè)計(jì)用例。錯(cuò)誤推測需要測試設(shè)計(jì)者比較熟悉業(yè)務(wù)邏輯,并且經(jīng)驗(yàn)豐富。
其他還有因果圖、正交法等方法,這里就不說了。
覆蓋率
如果按照前面的用例設(shè)計(jì)方法,可能會(huì)設(shè)計(jì)出很多用例。我們不可能也沒有必要把每一個(gè)用例都寫成單元測試。
怎么確認(rèn)用例是否足夠呢?一個(gè)很重要的參考指標(biāo)就是代碼覆蓋率。
覆蓋率指標(biāo)
常用的覆蓋率指標(biāo)有四種:
語句覆蓋:每條語句至少執(zhí)行一次。
分支覆蓋:每個(gè)分支至少有一次為真、一次為假。
條件覆蓋:每個(gè)分支的每個(gè)條件至少有一次為真、一次為假。
路徑覆蓋:對(duì)所有的分支、循環(huán)等可能的路徑,至少都要覆蓋一次。
我們以這個(gè)簡單的代碼為例,看看這四種覆蓋率到底是什么意思。
if (a && b) {
// X
}
// Y
if (c || d) {
// X
}
語句覆蓋。只需要一個(gè)測試用例,讓 a && b 和 c || d 都為真,系統(tǒng)會(huì)依次執(zhí)行 X、Y、Z 三個(gè)的代碼段,就能做到語句覆蓋。
分支覆蓋。至少需要兩個(gè)測試用例,讓 a && b 和 c || d 都各為真假,例如用例1 a && b 為真和 c || d 為假,用例2 則反過來,既可讓兩個(gè)條件分支都各為真一次,為假一次。
條件覆蓋。至少需要四個(gè)測試用例,條件 a 和 b 的四種組合都要執(zhí)行一次,條件 c 和 d 的四種組合也都要執(zhí)行一次。
路徑覆蓋。至少需要八個(gè)測試用例,條件 a、b、c 和 d 的所有組合都要執(zhí)行一次。
可以看到,要做到條件覆蓋甚至路徑覆蓋,會(huì)需要非常多的測試用例。一般情況,對(duì)于復(fù)雜的邏輯,單元測試做到分支覆蓋就不錯(cuò)了,必要的話再做更多完全的覆蓋。
Jacoco 覆蓋
Jacoco 的覆蓋率略有不同,這里簡單說一下。
指令覆蓋(Instructions),覆蓋所有的 Java 代碼指令。
分支覆蓋(Branches),和上面的分支覆蓋基本是一樣的。
圈復(fù)雜度覆蓋(Cyclomatic Complexity),可以認(rèn)為就是路徑覆蓋率。
語句覆蓋(Lines),和上面的語句覆蓋基本是一樣的。
方法覆蓋(Methods),覆蓋所有的方法。
類覆蓋(Classes),覆蓋所有的類。
怎么寫有效的單元測試?
到現(xiàn)在,相信大家對(duì)怎么寫單元測試應(yīng)該有一定概念了。但是很多人也會(huì)有疑問:
單元測試耗費(fèi)太多時(shí)間,會(huì)不會(huì)降低生產(chǎn)效率?
單元測試會(huì)不會(huì)很難維護(hù)?比如修改代碼時(shí)還總是需要修改單元測試。
關(guān)于第一個(gè)問題,相信大家應(yīng)該都能理解,如果我們?cè)陂_發(fā)時(shí)發(fā)現(xiàn) BUG,那么解決它是很容易的;但是一旦到了集成、驗(yàn)收甚至上線之后,那么要解決它就要花費(fèi)比較大的代價(jià)了。業(yè)界很早就有共識(shí),并且有不少數(shù)據(jù)可以證明,有效的單元測試雖然要花費(fèi)更多編碼時(shí)間,但是可以很大的減少項(xiàng)目的集成、測試和維護(hù)成本。
注意上面提到很重要一點(diǎn)是,單元測試必須是有效的,如果我們發(fā)現(xiàn)單元測試很難維護(hù),那往往是因?yàn)槲覀儧]有寫出有效的單元測試。
不是所有的代碼都需要單元測試
寫單元測試我們也需要考慮投入產(chǎn)出比,例如下面這些情況,寫單元測試的投入產(chǎn)出比可能會(huì)較差。
短期的或者一次性的項(xiàng)目,例如 Demo、數(shù)據(jù)更新腳本。
業(yè)務(wù)簡單的,不含太多邏輯的模塊。例如獲取或者查找一個(gè)數(shù)據(jù),或者沒有分支條件的業(yè)務(wù)邏輯等。
UI 層,相對(duì)而言比較難做單元測試,除非 UI 本身就有比較復(fù)雜的邏輯(其實(shí)某些 UI 框架也提供了單元測試工具)。
那么那些情況下要寫單元測試呢?簡單來說,就是兩類。
邏輯復(fù)雜、不容易理解、容易出錯(cuò)的模塊。例如,計(jì)算閏年的方法、訂單下單等。
公共模塊或者核心的業(yè)務(wù)模塊。
即使對(duì)于需要寫單元測試的模塊,我們也應(yīng)該關(guān)注最核心最重要的測試用例,而沒必要單純的追求覆蓋率,或者追求條件覆蓋甚至路徑覆蓋,一般做到分支覆蓋就可以了。另外一個(gè)有效的方法是,對(duì)于出現(xiàn)的每一個(gè) BUG,添加一個(gè)單元測試。
單元測試應(yīng)該是穩(wěn)定的
這里穩(wěn)定的第一個(gè)含義是,單元測試不應(yīng)該經(jīng)常需要修改。如果單元測試經(jīng)常因?yàn)榈讓訉?shí)現(xiàn)邏輯的變動(dòng)而需要修改,那一定不是好的單元測試。也就是說,被測單元的接口應(yīng)該是穩(wěn)定的、設(shè)計(jì)良好的、易于擴(kuò)展的。
穩(wěn)定的第二個(gè)含義是,單元測試的結(jié)果應(yīng)該是穩(wěn)定的。如果在不同的環(huán)境、不同的情況運(yùn)行單元測試,會(huì)返回不同的結(jié)果,那就不是好的單元測試。如果測試需要依賴特定的數(shù)據(jù)、文件等,那需要有前置的初始化腳本確保依賴的數(shù)據(jù)、文件在所有環(huán)境都存在并且是一致的。
單元測試應(yīng)該是灰盒測試
單元測試應(yīng)該覆蓋核心邏輯的各種分支、邊界及異常,但是避免涉及易變的實(shí)現(xiàn)邏輯。也就是說,我們不應(yīng)該把單元測試當(dāng)成完全的白盒測試,但也不是黑盒測試,而應(yīng)該把它當(dāng)成介于白盒和黑盒之間的灰盒測試。
被測代碼應(yīng)該是抽象良好的
如果我們發(fā)現(xiàn)一段代碼很難編寫單元測試,常常是因?yàn)檫@段代碼沒有符合良好的抽象規(guī)范,比如沒有使用 DI、不符合單一職責(zé)原則、或者依賴了全局的公共變量和方法等等。我們可以考慮優(yōu)化這段代碼,再來嘗試單元單元測試。
談?wù)劦降资裁词浅橄螅约败浖O(shè)計(jì)的抽象原則 介紹了軟件抽象的原則,這里就不再重復(fù)了。
編碼時(shí)就應(yīng)該同時(shí)寫好單元測試
這樣我們才能在調(diào)試時(shí)就發(fā)揮單元測試的優(yōu)勢,對(duì)代碼的任何修改都能得到即時(shí)反饋。如果是后面再補(bǔ)充單元測試,一方面對(duì)實(shí)現(xiàn)可能已經(jīng)不太熟悉了,編寫測試的代價(jià)更大了;另一方面,單元測試能發(fā)揮的作用也變小了。不過即使這樣,對(duì)那些需要長遠(yuǎn)維護(hù)的項(xiàng)目,編寫單元測試也還是很有用的。
單元測試的代碼質(zhì)量也很重要
單元測試也是代碼,也是需要不斷維護(hù)的。所以我們不應(yīng)該隨隨便便的去寫單元測試,而是要把他們也當(dāng)成普通代碼一樣,要做到高質(zhì)量、模塊化、可維護(hù)。
為什么要寫單元測試之終極原因
終極原因是,作為一名優(yōu)秀的工程師,如果被 QA 和產(chǎn)品經(jīng)理 Challenge 有 BUG,能忍嗎?而我們工程師當(dāng)然要用工程師 Style 的測試方法,那就是自動(dòng)化的單元測試了,不是嗎?
文章來源:網(wǎng)絡(luò) 版權(quán)歸原作者所有文章來源:http://www.zghlxwxcb.cn/news/detail-653180.html
上文內(nèi)容不用于商業(yè)目的,如涉及知識(shí)產(chǎn)權(quán)問題,請(qǐng)權(quán)利人聯(lián)系小編,我們將立即處理文章來源地址http://www.zghlxwxcb.cn/news/detail-653180.html
到了這里,關(guān)于軟件測試技術(shù)之單元測試—工程師 Style 的測試方法(3)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!