推薦JUnit視頻教程:JUnit—Java單元測試必備工具。
1.基本理論
1.1 單元測試
單元測試又稱模塊測試,屬於白盒測試,是最小單位的測試。模塊分為程序模塊和功能模塊。功能模塊指實現了一個完整功能的模塊(單元),一個完整的程序單元具備輸入、加工和輸出三個環節。而且每個程序單元都應該有正規的規格說明,使之對其輸入、加工和輸出的關系做出名明確的描述。
JUnit是一個回歸測試框架(regression testing framework)。Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。Junit是一套框架,繼承TestCase類,就可以用Junit進行自動測試了。
1.2 什么是Junit
①JUnit是用於編寫可復用測試集的簡單框架,是xUnit的一個子集。xUnit是一套基於測試驅動開發的測試框架,有PythonUnit、CppUnit、JUnit等。
②Junit測試是程序員測試,即所謂白盒測試,因為程序員知道被測試的軟件如何(How)完成功能和完成什么樣(What)的功能。
③多數Java的開發環境都已經集成了JUnit作為單元測試的工具,比如IDEA,Eclipse等等。
④JUnit官網:Junit
1.3 為什么要使用單元測試
①測試框架可以幫助我們對編寫的程序進行有目的地測試,幫助我們最大限度地避免代碼中的bug,以保證系統的正確性和穩定性。
②很多人對自己寫的代碼,測試時就簡單寫main,然后sysout輸出控制台觀察結果。這樣非常枯燥繁瑣,不規范。缺點:測試方法不能一起運行,測試結果要程序猿自己觀察才可以判斷程序邏輯是否正確。
③JUnit的斷言機制,可以直接將我們的預期結果和程序運行的結果進行一個比對,確保對結果的可預知性。
1.4 測試覆蓋
評測測試過程中已經執行的代碼的多少。
1.5 代碼覆蓋率
代碼的覆蓋程度,一種度量方式。針對代碼的測試覆蓋率有許多種度量方式。
語句覆蓋( StatementCoverage ):也稱為行覆蓋( lin EC overage ) , 段覆蓋(segmentcoverage)和基本塊覆蓋(bASicblockcoverage)。它度量每一個可執行語句是否被執行到了。
判定覆蓋(DecisionCoverage):也被稱為分支覆蓋(branchcoverage),所有邊界覆蓋(alledgescoverage), 基本路徑覆蓋( basispathcoverage ), 判定路徑覆蓋(decisiondecisionpath或DDPtesting)。它度量是否每個 BOOL 型的表達式取值true 和 false 在控制結構中都被測試到了。
條件覆蓋(ConDItionCoverage): 它獨立的度量每一個子表達式,報告每一個子表達式的結果的 true 或 false。這個度量和判定覆蓋(decisioncoverage)相似,但是對控制流更敏感。不過,完全的條件覆蓋並不能保證完全的判定覆蓋。
路徑覆蓋(PathCoverage):也稱為斷言覆蓋(prEDIcatecoverage),它度量了是否函數的每一個可能的分支都被執行了。路徑覆蓋的一個好處是:需要徹底的測試。但有兩個缺點:一是,路徑是以分支的指數級別增加的,例如:一個函數包含 10個 IF 語句,就有 1024 個路徑要測試。如果加入一個 IF 語句,路徑數就達到 2048;二是,許多路徑不可能與執行的數據無關。
- 循環覆蓋(LOOPCoverage):這個度量報告你是否執行了每個循環體零次、只有一次還是多余一次(連續地)。對於 dowhile循環,循環覆蓋報告你是否執行了每個循環體只有一次還是多余一次(連續地)。這個度量的有價值的方面是確定是否對於 while 循環和 for 循環執行了多於一次,這個信息在其它的覆蓋率報告中是沒有的。
2. IDEA中Junit 4的配置
2.1 環境配置
IDEA中自帶JUnit的jar包(hamcrest-core-1.3.jar
,junit.jar
,junit-4.12.jar
):位於安裝目錄下的lib文件內。即:IntelliJ IDEA 2018.1\lib
IDEA自帶一個JUnit插件, 該插件可以運行JUnit測試文件,但無法自動生成JUnit測試代碼. 如果需要自動生成測試代碼,需要安裝JUnitGenerator V2.0
插件。
安裝方法:File
-->settings
-->Plguins
-->Browse repositories
-->輸入JUnit
-->選擇JUnit Generator V2.0
安裝,安裝好后重啟 IDEA,即可使用。
PS: IDEA自帶的JUnit插件和JUnitGeneratorV2.0插件都要勾選上,若只勾選JUnit可能導致無法自動生成測試文件,若只勾選JUnitGenerator V2.0可能導致生成的測試文件無法運行
2.2 修改配置
-
通過模版生成的測試代碼與java類在同一個目錄下,與maven項目標准測試目錄不匹配。修改方法:
${SOURCEPATH}/test/${PACKAGE}/${FILENAME}
修改為${SOURCEPATH}/../test/java/${PACKAGE}/${FILENAME}
有時候會出現一個提示框,提示選擇(general/merge/exixt),選擇general就行${SOURCEPATH}
原類的路徑,就是你自己寫的類
${PACKAGE}
原類的包名,java包命名規范就是按照包名一級一級創建的文件夾
${filename}
測試類的類名
-
修改 Junit4 選項卡中包的申明,把默認的 test 前綴去掉。
-
生成的測試文件@since位置Date可能存在亂碼,可配置JUnit模板更改日期格式,不影響程序可直接忽略。修改方法:
第40行:@since <pre>$Date</pre>
修改為
@since <pre>$today</pre>
3. 簡單入門單元測試
在婁老師的博客文章中講了單元測試,我們根據老師的方法來進行簡單入門單元測試。
什么是單元測試?單元測試(unit testing),是指對軟件中的最小可測試單元進行檢查和驗證。對於單元測試中單元的含義,一般來說,要根據實際情況去判定其具體含義,如Java里單元指一個類。
編程是智力活動,不是打字,編程前要把干什么、如何干想清楚才能把程序寫對、寫好。與目前不少同學一說編程就打開編輯器寫代碼不同,我希望同學們養成一個習慣,當你們想用程序解決問題時,要會寫三種碼:
- 偽代碼
- 產品代碼
- 測試代碼
偽代碼與具體編程語言無關,不要寫與具體編程語言語法相關的語句(如
用malloc分配內存
,這樣只能用C語言編程了),偽代碼
從意圖層面來解決問題,最終,偽代碼
是產品代碼
最自然的、最好的注釋。
需求:我們要在一個Calculator
類中來實現簡單計算器的功能。
3.1 偽代碼
設計的偽代碼:
簡單計算器:
加法:返回兩個數相加后的結果
減法:返回兩個數相減后的結果
乘法:返回兩個數相乘后的結果
除法:除數不為0,返回兩個數相除后的結果
除數為0,打印異常信息在程序中出錯的位置及原因
其他 :未完成的模塊例如平方、開方等等
3.2 產品代碼
有了偽代碼
,用特定語言翻譯一下,就是可用的產品代碼
了。
翻譯之后的產品代碼Calculator.java
:
public class Calculator {
public int add(int x, int y) { //加法
return x + y;
}
public int sub(int x, int y) { //減法
return x - y;
}
public int mul(int x, int y) { //乘法
return x * y;
}
public int div(int x, int y) { //除法
return x / y;
}
public int div2(int x, int y) { //除法 做了異常判斷
try {
int z = x / y;
} catch (Exception e) {
e.printStackTrace();
}
return x / y;
}
public void loop(int x, int y) { //死循環
for (; ; )
x = y;
}
public void unCompleted(int x, int y) { //未完成的模塊:例如平方、開方等等
//還在開發中
}
// public static void main(String[] args) { // 傳統代碼測試
// int a = 8;
// int b = 2;
// Calculator calculator = new Calculator();
// System.out.println(calculator.add(a, b));
// System.out.println(calculator.sub(a, b));
// System.out.println(calculator.mul(a, b));
// System.out.println(calculator.div(a, b));
// System.out.println(calculator.div2(a,0));
// }
}
在IDEA中創建項目unitTask01
,在src
右鍵新建java類com.ryanjie.junit01.Calculator
,將上述代碼寫入。
3.3 測試代碼
寫了產品代碼,我們還要寫測試代碼,證明自己的代碼沒有問題。
Java編程時,程序員對類實現的測試叫單元測試。類XXXX的單元測試,我們一般寫建一個XXXXTest的類。
所以,對於Calculator類
,我們寫一個CalculatorTest.java
的測試模塊,代碼如下:
public class CalculatorTest {
public static void main(String[] args) {
Calculator calculator = new Calculator();
if (calculator.add(8, 2) == 10) {
System.out.println("Test passed!");
} else {
System.out.println("Test failed!");
}
if (calculator.sub(8, 2) == 6) {
System.out.println("Test passed!");
} else {
System.out.println("Test failed!");
}
if (calculator.mul(8, 2) == 16) {
System.out.println("Test passed!");
} else {
System.out.println("Test failed!");
}
if (calculator.div(8, 2) == 4) {
System.out.println("Test passed!");
} else {
System.out.println("Test failed!");
}
}
}
這里我們設計了一個測試用例(Test Case)
,測試用例是為某個特殊目標而編制的一組測試輸入、執行條件以及預期結果,以便測試某個程序路徑或核實是否滿足某個特定需求。這里我們的測試輸入8和2的加減乘除四種運算
,預期結果分別為10,6,16,4
。
我們在Calculator
類上單擊鼠標右鍵,在彈出的菜單中選擇Generate
(我們可以看到對應鍵盤快捷鍵為Alt+Insert
)-> Junit Test
-> Junit 4
,這時IDEA會自動生成一個test目錄並生成CalculatorTest類,我們先刪除自動生成的代碼,輸入剛才寫的CalculatorTest.java
代碼。
在 IDEA中我們把產品代碼放在src
目錄中,把測試代碼放在test
目錄中,在命令行中我們知道要設置SOURCEPATH環境變量,在IDEA中我們右鍵單擊test
目錄,在彈出的菜單中選擇Mark Directory as
->Test Sources Root
就可以了:(這時test文件夾圖標會變綠)
在IDEA中運行結果如下,測試結果符合預期:
3.4 測試代碼2 (使用Junit)
在剛才自動生成的test
目錄下,CalculatorTest.java
里代碼如下:
public class CalculatorTest {
@Before
public void before() throws Exception {
}
@After
public void after() throws Exception {
}
/**
*
* Method: add(int x, int y)
*
*/
@Test
public void testAdd() throws Exception {
}
/**
*
* Method: sub(int x, int y)
*
*/
@Test
public void testSub() throws Exception {
}
/**
*
* Method: mul(int x, int y)
*
*/
@Test
public void testMul() throws Exception {
}
/**
*
* Method: div(int x, int y)
*
*/
@Test
public void testDiv() throws Exception {
}
/**
*
* Method: div2(int x, int y)
*
*/
@Test
public void testDiv2() throws Exception {
}
/**
*
* Method: loop(int x, int y)
*
*/
@Test
public void testLoop() throws Exception {
}
/**
*
* Method: unCompleted(int x, int y)
*
*/
@Test
public void testUnCompleted() throws Exception {
}
}
我們修改CalculatorTest.java
測試模塊,代碼如下:
public class CalculatorTest {
Calculator calculator;
@Before
public void setUp() throws Exception {
calculator = new Calculator();
}
@After
public void after() throws Exception {
}
/**
*
* Method: add(int x, int y)
*
*/
@Test
public void testAdd() throws Exception {
Assert.assertEquals(calculator.add(8,2),10);
}
/**
*
* Method: sub(int x, int y)
*
*/
@Test
public void testSub() throws Exception {
Assert.assertEquals(calculator.sub(8,2),6);
}
/**
*
* Method: mul(int x, int y)
*
*/
@Test
public void testMul() throws Exception {
Assert.assertEquals(calculator.mul(8,2),16);
}
/**
*
* Method: div(int x, int y)
*
*/
@Test
public void testDiv() throws Exception {
Assert.assertEquals(calculator.div(8,2),4);
}
/**
*
* Method: div2(int x, int y)
*
*/
@Ignore
public void testDiv2() throws Exception {
}
/**
*
* Method: loop(int x, int y)
*
*/
@Ignore
public void testLoop() throws Exception {
}
/**
*
* Method: unCompleted(int x, int y)
*
*/
@Ignore
public void testUnCompleted() throws Exception {
}
}
在IDEA中運行結果如下,測試結果符合預期:
3.5 編輯測試設置
我們可以通過Run
->Edit Configuration
或工具欄上的標簽來調整我們測試運行配置:
在Configuration
選項卡,用戶可以選擇需要運行的測試。例如,您可以從一個類、程序包、測試套件或甚至模式中運行所有的測試。這里的Fork模式讓用戶在一個單獨的進程運行每個測試。
代碼覆蓋測試:
- 默認情況下使用自己的測試引擎
- 覆蓋率模式:Tracing會增加消耗,但是測試會更准確
3.6 收集覆蓋率
過Run
->Run 'MyClassTest' with Coverage
或工具欄上的選項運行特定模式的測試。
當覆蓋模式運行至少一個測試之后,IDE將會在Project工具窗口顯示每個程序包、類的覆蓋率數據,同時在Coverage工具窗和編輯器中也會顯示。
如果用戶添加另一個方法到junit01
,並運行覆蓋率測junit01
,就會發現,沒有被測試覆蓋到的代碼都將高亮顯示為紅色。覆蓋的代碼顏色則是綠色。如果一些代碼是只覆蓋部分,那沒將顯示為黃色。
本文代碼:UnitTask01