JUnit 是 Java? 語言事實上的 標准單元測試庫
JUnit
是該庫三年以來最具裡程碑意義的一次發布
它的新特性主要是通過采用 Java
中的標記(annotation)而不是利用子類
反射或命名機制來識別測試
從而簡化測試
在本文中
執著的代碼測試人員 Elliotte Harold 以 JUnit
為例
詳細介紹了如何在自己的工作中使用這個新框架
注意
本文假設讀者具有 JUnit 的使用經驗
JUnit 由 Kent Beck 和 Erich Gamma 開發
幾乎毫無疑問是迄今所開發的最重要的第三方 Java 庫
正如 Martin Fowler 所說
在軟件開發領域
從來就沒有如此少的代碼起到了如此重要的作用
JUnit 引導並促進了測試的盛行
由於 JUnit
Java 代碼變得更健壯
更可靠
bug 也比以前更少
JUnit(它本身的靈感來自 Smalltalk 的 SUnit)衍生了許多 xUnit 工具
將單元測試的優勢應用於各種語言
nUnit (
NET)
pyUnit (Python)
CppUnit (C++)
dUnit (Delphi) 以及其他工具
影響了各種平台和語言上的程序員的測試工作
然而
JUnit 僅僅是一個工具而已
真正的優勢來自於 JUnit 所采用的思想和技術
而不是框架本身
單元測試
測試先行的編程和測試驅動的開發並非都要在 JUnit 中實現
任何比較 GUI 的編程都必須用 Swing 來完成
JUnit 本身的最後一次更新差不多是三年以前了
盡管它被證明比大多數框架更健壯
更持久
但是也發現了 bug
而更重要的是
Java 不斷在發展
Java 語言現在支持泛型
枚舉
可變長度參數列表和注釋
這些特性為可重用的框架設計帶來了新的可能
JUnit 的停滯不前並沒有被那些想要廢棄它的程序員所打敗
挑戰者包括 Bill Venners 的 Artima SuiteRunner 以及 Cedric Beust 的 TestNG 等
這些庫有一些可圈可點的特性
但是都沒有達到 JUnit 的知名度和市場占有份額
它們都沒有在諸如 Ant
Maven 或 Eclipse 之類的產品中具有廣泛的開箱即用支持
所以 Beck 和 Gamma 著手開發了一個新版本的 JUnit
它利用 Java
的新特性(尤其是注釋)的優勢
使得單元測試比起用最初的 JUnit 來說更加簡單
用 Beck 的話來說
JUnit
的主題是通過進一步簡化 JUnit
鼓勵更多的開發人員編寫更多的測試
JUnit
盡管保持了與現有 JUnit
測試套件的向後兼容
但是它仍然承諾是自 JUnit
以來 Java 單元測試方面最重大的改進
注意
該框架的改進是相當前沿的
盡管 JUnit
的大輪廓很清晰
但是其細節仍然可以改變
這意味著本文是對 JUnit
搶先看
而不是它的最終效果
測試方法 以前所有版本的 JUnit 都使用命名約定和反射來定位測試
例如
下面的代碼測試
+
等於
import junit
framework
TestCase;
public class AdditionTest extends TestCase {
private int x =
;
private int y =
;
public void testAddition() {
int z = x + y;
assertEquals(
z);
}
}
而在 JUnit
中
測試是由 @Test 注釋來識別的
如下所示
import org
junit
Test;
import junit
framework
TestCase;
public class AdditionTest extends TestCase {
private int x =
;
private int y =
;
@Test public void testAddition() {
int z = x + y;
assertEquals(
z);
}
}
使用注釋的優點是不再需要將所有的方法命名為 testFoo()
testBar()
等等
例如
下面的方法也可以工作
import org
junit
Test;
import junit
framework
TestCase;
public class AdditionTest extends TestCase {
private int x =
;
private int y =
;
@Test public void additionTest() {
int z = x + y;
assertEquals(
z);
}
}
下面這個方法也同樣能夠工作
import org
junit
Test;
import junit
framework
TestCase;
public class AdditionTest extends TestCase {
private int x =
;
private int y =
;
@Test public void addition() {
int z = x + y;
assertEquals(
z);
}
}
這允許您遵循最適合您的應用程序的命名約定
例如
我介紹的一些例子采用的約定是
測試類對其測試方法使用與被測試的類相同的名稱
例如
ntains() 由 ntains() 測試
List
add() 由 ListTest
addAll() 測試
等等
TestCase 類仍然可以工作
但是您不再需要擴展它了
只要您用 @Test 來注釋測試方法
就可以將測試方法放到任何類中
但是您需要導入 junit
Assert 類以訪問各種 assert 方法
如下所示
import org
junit
Assert;
public class AdditionTest {
private int x =
;
private int y =
;
@Test public void addition() {
int z = x + y;
Assert
assertEquals(
z);
}
}
您也可以使用 JDK
中新特性(static import)
使得與以前版本一樣簡單
import static org
junit
Assert
assertEquals;
public class AdditionTest {
private int x =
;
private int y =
;
@Test public void addition() {
int z = x + y;
assertEquals(
z);
}
}
這種方法使得測試受保護的方法非常容易
因為測試案例類現在可以擴展包含受保護方法的類了
SetUp 和 TearDown JUnit
測試運行程序(test runner)會在運行每個測試之前自動調用 setUp() 方法
該方法一般會初始化字段
打開日志記錄
重置環境變量
等等
例如
下面是摘自 XOM 的 XSLTransformTest 中的 setUp() 方法
protected void setUp() {
System
setErr(new PrintStream(new ByteArrayOutputStream()));
inputDir = new File(
data
);
inputDir = new File(inputDir
xslt
);
inputDir = new File(inputDir
input
);
}
在 JUnit
中
您仍然可以在每個測試方法運行之前初始化字段和配置環境
然而
完成這些操作的方法不再需要叫做 setUp()
只要用 @Before 注釋來指示即可
如下所示
@Before protected void initialize() {
System
setErr(new PrintStream(new ByteArrayOutputStream()));
inputDir = new File(
data
);
inputDir = new File(inputDir
xslt
);
inputDir = new File(inputDir
input
);
}
甚至可以用 @Before 來注釋多個方法
這些方法都在每個測試之前運行
@Before protected void findTestDataDirectory() {
inputDir = new File(
data
);
inputDir = new File(inputDir
xslt
);
inputDir = new File(inputDir
input
);
}
@Before protected void redirectStderr() {
System
setErr(new PrintStream(new ByteArrayOutputStream()));
}
清除方法與此類似
在 JUnit
中
您使用 tearDown() 方法
該方法類似於我在 XOM 中為消耗大量內存的測試所使用的方法
protected void tearDown() {
doc = null;
System
gc();
}
對於 JUnit
我可以給它取一個更自然的名稱
並用 @After 注釋它
@After protected void disposeDocument() {
doc = null;
System
gc();
}
與 @Before 一樣
也可以用 @After 來注釋多個清除方法
這些方法都在每個測試之後運行
最後
您不再需要在超類中顯式調用初始化和清除方法
只要它們不被覆蓋即可
測試運行程序將根據需要自動為您調用這些方法
超類中的 @Before 方法在子類中的 @Before 方法之前被調用(這反映了構造函數調用的順序)
@After 方法以反方向運行
子類中的方法在超類中的方法之前被調用
否則
多個 @Before 或 @After 方法的相對順序就得不到保證
套件范圍的初始化 JUnit
也引入了一個 JUnit
中沒有的新特性
類范圍的 setUp() 和 tearDown() 方法
任何用 @BeforeClass 注釋的方法都將在該類中的測試方法運行之前剛好運行一次
而任何用 @AfterClass 注釋的方法都將在該類中的所有測試都運行之後剛好運行一次
例如
假設類中的每個測試都使用一個數據庫連接
一個網絡連接
一個非常大的數據結構
或者還有一些對於初始化和事情安排來說比較昂貴的其他資源
不要在每個測試之前都重新創建它
您可以創建它一次
並還原它一次
該方法將使得有些測試案例運行起來快得多
例如
當我測試調用第三方庫的代碼中的錯誤處理時
我通常喜歡在測試開始之前重定向 System
err
以便輸出不被預期的錯誤消息打亂
然後我在測試結束後還原它
如下所示
// This class tests a lot of error conditions
which
// Xalan annoyingly logs to System
err
This hides System
err
// before each test and rest
From:http://tw.wingwit.com/Article/program/Java/ky/201311/28884.html