提要 本文將向你介紹如何實現從JUnit 3.8向JUnit 4的遷移。同時,還討論JUnit 4中的一些新特徵,特別包括對註解的使用;最後,簡要介紹這個新版本的IDE集成現狀。
一、 引言
在本文開始,我將假定,你已經瞭解由Kent Beck和Erich Gamma發明的這個Java單元測試框架並因此而略過必要的簡介。所以,我將集中分析從JUnit 3.8到最新版本-JUnit 4的遷移過程以及其在IDE和Ant中的集成。
JUnit 4是一種與其之前的版本完全不同的API,它根據Java 5.0中的新特徵(註解,靜態導入等)構建而成。如你所見,JUnit 4更簡單、更豐富和更易於使用,而且它引入了更爲靈活的初始化和清理工作,還有限時的和參數化測試用例。
代碼實例最能說明問題。因此,在本文中,我將使用一個例子來展示不同的測試用例:一個計算器。該示例計算器很簡單,效率並不高,甚至還有一些錯誤;它僅 僅操作整數,並且把結果存儲在一個靜態變量中。Substract方法並不返回一個有效的結果,而且也沒有實現乘法運算,而且看上去在 squareRoot方法中還存在一個錯誤:無限循環。這些錯誤將幫助說明使用JUnit 4進行測試的有效性。你可以打開和關閉這個計算器,而且你可以清除這些結果。下面是其實現代碼:
package calc; public class Calculator { private static int result; //存儲結果的靜態變量 public void add(int n) { result = result + n; } public void substract(int n) { result = result - 1; //錯誤:應該是 "result = result - n" } public void multiply(int n) {} //還沒實現 public void divide(int n) { result = result / n; } public void square(int n) { result = n * n; } public void squareRoot(int n) { for (; ;) ; //錯誤:無限循環 } public void clear() { //清除結果 result = 0; } public void switchOn() { //打開屏幕,顯示 "hello",並報警 result = 0; //實現其它的計算器功能 } public void switchOff() { } //顯示 "bye bye",報警,並關閉屏幕 public int getResult() { return result; } } |
二、 遷移一個測試類
現在,我將把一個已經使用JUnit 3.8編寫成的簡單的測試類遷移到JUnit 4。這個類有一些缺陷:它沒有測試所有的業務方法,而且看上去在testDivide方法中還存在一個錯誤(8/2不等於5)。因爲還沒有實現乘法運算功能,所以對其測試將被忽略。
下面,我們把兩個版本的框架之間的差別以粗體顯示出現於表格1中。
表格1.分別以JUnit 3.8和JUnit 4實現的CaculatorTest。
JUnit 3.8
package junit3; import calc.Calculator; import junit.framework.TestCase; public class CalculatorTest extends TestCase { private static Calculator calculator = new Calculator(); @Override protected void setUp() { calculator.clear(); } public void testAdd() { calculator.add(1); calculator.add(1); assertEquals(calculator.getResult(), 2); } public void testSubtract() { calculator.add(10); calculator.subtract(2); assertEquals(calculator.getResult(), 8); } public void testDivide() { calculator.add(8); calculator.divide(2); assert calculator.getResult() == 5; } public void testDivideByZero() { try { calculator.divide(0); fail(); } catch (ArithmeticException e) { } } public void notReadyYetTestMultiply() { calculator.add(10); calculator.multiply(10); assertEquals(calculator.getResult(), 100); } } |
JUnit 4
package JUnit 4; import calc.Calculator; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; public class CalculatorTest { private static Calculator calculator = new Calculator(); @Before public void clearCalculator() { calculator.clear(); } @Test public void add() { calculator.add(1); calculator.add(1); assertEquals(calculator.getResult(), 2); } @Test public void subtract() { calculator.add(10); calculator.subtract(2); assertEquals(calculator.getResult(), 8); } @Test public void divide() { calculator.add(8); calculator.divide(2); assert calculator.getResult() == 5; } @Test(expected = ArithmeticException.class) public void divideByZero() { calculator.divide(0); } @Ignore( "not ready yet") @Test public void multiply() { calculator.add(10); calculator.multiply(10); assertEquals(calculator.getResult(), 100); } } |
首先,你可以看到,JUnit 4使用org.junit.*包而JUnit 3.8使用的是junit.framework.*。當然,爲了向後兼容性起見,JUnit 4jar文件發行中加入了這兩種包。
四、 繼承
在中,測試類不必再擴展junit.framework.TestCase;事實上,它們不必須擴展任何內容。但是,JUnit 4中使用的是註解。爲了以一個測試用例方式執行,一個JUnit 4類中至少需要一個@Test註解。例如,如果你僅使用@Before和@After註解而沒有至少提供一個@Test方法來編寫一個類,那麼,當你試圖 執行它時將得到一個錯誤:
java.lang.Exception: No runnable methods.
五、 斷言(Assert)方法
因爲在JUnit 4中一個測試類並不繼承自TestCase(在JUnit 3.8中,這個類中定義了assertEquals()方法),所以你必須使用前綴語法(舉例來說,Assert.assertEquals())或者 (由於JDK5.0)靜態地導入Assert類。這樣以來,你就可以完全象以前一樣使用assertEquals方法(舉例來說, assertEquals())。
另外,在JUnit 4中,還引入了兩個新的斷言方法,它們專門用於數組對象的比較。如果兩個數組包含的元素都相等,那麼這兩個數組就是相等的。
public static void assertEquals(String message, Object[] expecteds, Object[] actuals); public static void assertEquals(Object[] expecteds, Object[] actuals); |
由於JDK 5.0的自動裝箱機制的出現,原先的12個assertEquals方法全部去掉了。例如,原先JUnit 3.8中的assertEquals(long,long)方法在JUnit 4中要使用assertEquals(Object,Object)。對於assertEquals(byte,byte)、assertEquals (int,int)等也是這樣。這種改進將有助於避免反模式。
在JUnit 4中,新集成了一個assert關鍵字(見我們的例子中的divide()方法)。你可以象使用assertEquals方法一樣來使用它,因爲它們都拋 出相同的異常(java.lang.AssertionError)。JUnit 3.8的assertEquals將拋出一個junit.framework.AssertionFailedError。注意,當使用assert時, 你必須指定Java的"-ea"參數;否則,斷言將被忽略。
六、 預設環境(Fixture)
Fixture是在測試期間初始化和釋放任何普通對象的方法。在JUnit 3.8中,你要使用setUp()來實現運行每一個測試前的初始化工作,然後使用tearDown()來進行每個測試後的清理。這兩個方法在 TestCase類中都得到重載,因此都被唯一定義。注意,我在這個Setup方法使用的是Java5.0內置的@Override註解-這個註解指示該 方法聲明要重載在超類中的方法聲明。在JUnit 4中,則代之使用的是@Before和@After註解;而且,可以以任何命名(在我們的例子中是clearCalculator())來調用這些方法