Java 單元測試利器之 Junit
前言:
因為工作和學習的需要,在程式碼中查錯的時候,第一步就是想知道這個錯誤具體發生在一個位置,進行一個準確的定位。而這個定位的工作交給誰來做了呢?不難猜出也就是這篇部落格的主題---Junit。junit是一個開源的框架,也是java這一塊的測試工具之一。想了解詳細請上官網,下面用程式碼來跟大家解釋。
準備要測試的方法,放進一個類中方便於測試。
package com.junit3_8; /** * junt3.8單元測試 * * @author CHH * @since 2013-01-19 * */ public class Calculator {// 加 public int add(int a, int b) { int sum=0; for(int i=0;i<100;i++){ sum +=i; } System.out.println("加=="+sum); return a+b; } // 減 public int subtract(int a, int b) { int sum=0; for(int i=0;i<100;i++){ sum+=i; } System.out.println("減=="+sum); return a - b; } // 乘 public int multiply(int a, int b) { int sum=0; for(int i=0;i<100;i++){ sum +=i; } System.out.println("乘==="+sum); return a * b; }// 除 public int divide(int a, int b) throws Exception { int sum=0; for(int i=0;i<100;i++){ sum +=i; } System.out.println("除==="+sum); if (0 == b) { throw new Exception("除數不能為0"); } return a / b; } }
Junit3.8測試類
package com.junit3_8; import junit.framework.Assert; import junit.framework.TestCase; /** * * @author CHH * @since 2013-01-19 */ public class CalculatorTest extends TestCase { Calculator cal; //在“每個”測試方法執行之前被呼叫 public void setUp() { //這段程式碼在這寫比較方便,只寫一次就夠, //不用在每個方法裡寫,因為這個方法每次都被呼叫,生成不同的物件,供測試方法使用 cal = new Calculator(); } //在“每個”測試方法執行之後被呼叫 public void tearDown() { } //測試方法:方法名要以test為開頭,無引數,無返回型別 public void testAdd() { //Calculator cal = new Calculator(); int result = cal.add(1, 2); //第一個引數是預期的,第二個引數是真實的 Assert.assertEquals(3, result); //Assert.fail(); } public void testSubtract() { //Calculator cal = new Calculator(); int result = cal.subtract(1, 2); //第一個引數是預期的,第二個引數是真實的 Assert.assertEquals(-1, result); } public void testMultiply() { //Calculator cal = new Calculator(); int result = cal.multiply(1, 2); //第一個引數是預期的,第二個引數是真實的 Assert.assertEquals(2, result); } public void testDivide() { int result = 0; //Calculator cal = new Calculator(); try { result = cal.divide(4, 2); } catch(Exception e) { e.printStackTrace(); //讓測試失敗 Assert.fail(); } //第一個引數是預期的,第二個引數是真實的 Assert.assertEquals(2, result); } //除數為0的情況 public void testDivideByZero() { Throwable th = null ; //Calculator cal = new Calculator(); try { cal.divide(1, 0); Assert.fail(); } catch(Exception e) { th = e ; //e.printStackTrace(); } //th 不為空 null Assert.assertNotNull(th); //第一個引數是預期的,第二個引數是真實的 Assert.assertEquals(Exception.class, th.getClass()); Assert.assertEquals("除數不能為0", th.getMessage()); } //加了這個main方法,可以直接以 Java Application 方式執行 ,也可以以 JUnit Test 執行 public static void main(String[] args) { //命令列形式列印 junit.textui.TestRunner.run(CalculatorTest.class); //awt 介面方式顯示 //junit.awtui.TestRunner.run(CalculatorTest.class); //swing 介面方式顯示 //junit.swingui.TestRunner.run(CalculatorTest.class); } }
從上面的程式碼中可以看出Junit測試類是通過繼承一個TestCase類來實現方法的測試,而這就是Junit4.0以前的測試方式,而在4.0之後他們的實現方式又有了稍微的變化。
Junit4.0之後不再是通過繼承TestCase的方式來實現方法的實測,而是採用註解的方式進行的。根據Java 5.0中的新特徵(註解,靜態匯入等),Junit開發團隊也隨之靠攏,採用註解的方式來進行方法的測試。這樣下來相比而言JUnit 4更簡單、更豐富和更易於使用。
Junit4.0測試類
package com.junit4_0; import static org.junit.Assert.assertEquals; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; /** * Junit4.0 * @author CHH * @since 2013-01-19 晚上10:12 * */ public class CalculatorTest { private static Calculator calculator = new Calculator(); //每個方法測試前呼叫 @Before public void clearCalculator() { calculator.clear(); } //每個方法測試完以後呼叫 @After public void tearDown() { } //@Test:測試方法,表明這是一個測試方法。在Junit中將會自動被執行。 @Test public void add() { calculator.add(1); calculator.add(1); //第一個引數是預期的,第二個引數是真實的 assertEquals(calculator.getResult(), 2); } @Test public void subtract() { calculator.add(10); calculator.substract(2); //第一個引數是預期的,第二個引數是真實的 assertEquals(calculator.getResult(), 8); } //給測試函式設定一個執行時間,超過了這個時間(400毫秒),它們就會被系統強行終止 @Test(timeout=400) public void divide() { calculator.add(8); calculator.divide(2); //第一個引數是預期的,第二個引數是真實的 assert calculator.getResult() == 5; } //使用註釋來宣告該異常是預期的,異常測試是Junit4中的最大改進 @Test(expected = ArithmeticException.class) public void divideByZero() { calculator.divide(0); } //@Ignore:忽略的測試方法,標註的含義就是“某些方法尚未完成,暫不參與此次測試” @Ignore("not ready yet") @Test public void multiply() { calculator.add(10); calculator.multiply(10); //第一個引數是預期的,第二個引數是真實的 assertEquals(calculator.getResult(), 100); } }
Junit4出了採用的註解的方式來進行測試方法之外,還增加了一些新的元素。
A、JUnit4添加了兩個比較陣列的assert() 方法:
public static void assertEquals(Object[] expected, Object[] actual)
public static void assertEquals(String message, Object[] expected, Object[] actual)
Junit4常用註解:
@Test 測試方法,表明這是一個測試方法。在Junit中將會自動被執行。
@Test(timeOut=400) 給測試函式設定一個執行時間,超過了這個時間(400毫秒),它們就會被系統強行終止
@Test(expected = ArithmeticException.class) 使用註釋來宣告該異常是預期的,異常測試是Junit4中的最大改進
@Ignore("not ready yet") 忽略的測試方法,標註的含義就是“某些方法尚未完成,暫不參與此次測試”
@Before 每個方法測試前呼叫
@After 每個方法測試完以後呼叫
@BeforeClass 每個類執行前呼叫,並且只調用一次
@AfterClass 每個類執行後呼叫,並且只調用一次
表格[email protected]/@AfterClass比較於@Before/@After。
@BeforeClass和@AfterClass | @Before和@After |
在每個類中只有一個方法能被註解。 | 多個方法能被註解,但其執行的順序未特別指定,且不執行過載方法。 |
方法名是不相關的 | 方法名是不相關的 |
每個類執行一次 | 在每個測試方法執行前或執行後執行 |
在當前類的@BeforeClass方法執行前先執行超類的@BeforeClass方法。在超類中宣告的@AfterClass方法將在所有當前類的該方法執行後才執行。 | 超類中的@Before在所有子類的該方法執行前執行。在超類中的@After在在所有子類的該方法執行後才執行。 |
必須是公共和非靜態的。 | 必須是公共和非靜態的。 |
即使一個@BeforeClass方法丟擲一個異常,所有的@AfterClass方法也保證被執行。 | 即使一個@Before或者@Test方法丟擲一個異常,所有的@After方法也保證被執行。 |
總結:這兩個版本最大的區別在JUnit3.x中測試必須繼承 TestCase,並且每個方法名必須以test開頭。比如:testMethod1()而在JUnit4.x中不必繼承TestCase,採用了註解的 方式。只要在測試的方法上加上註解@Test即可,從而不必再遵循以前的一些顯式約定和反射定位測試;在JUnit4.x中如果繼承了TestCase, 註解就不起作用了。並且有很重要的一點就是在JUnit4.x中繼承了TestCase後,在OutLine檢視中測試單個方法時,結果整個類都run 了。還有一點就是,在3.x中需要實現setUp和tearDown方法,而在4.x中無需這樣,可以自定義需要在測試前和測試後的方法,在方法前加上 @before,@after就可以了。所以在JUnit4.x不必繼承TestCase用註解即可對單個方法進行測試。