JUnit 4 與 TestNG 對比
原文出處: 付學良的網志
這兩天在看一本書《Java測試新技術TestNG和高級概念》,作者是 TestNG 的創始人,了解了不少關于 TestNG 的知識,看了一篇文章基本把這本書的幾個觀點都體現了,那我就直接翻譯原文就好,省得自己總結。這兩天要不是等原作者的授權的話可能周末本文就發布了,沒經過人家許可翻譯人家文章應該的確也不是什么光彩的事情,等等無妨。原文鏈接JUnit 4 Vs TestNG – Comparison,非常感謝作者寫出好文,不過mkyong先生寫了的確好多文章,經常搜個文章 google 第一篇總是他的。如果有翻譯問題,請拍磚。
————————————————————————————————————————————
Junit 4 和 TestNG 都是 Java 方面非常流行的單元測試框架。在功能上兩個框架都非常類似。到底哪個比較好?在Java項目中我們應該選擇哪個框架?
下圖將會對Junit 4 和 TestNG 做個功能特征的對比。
注解支持
Junit 4 和 TestNG 在注解方面的實現非常相似。
| 特性 | JUnit 4 | TestNG |
| 測試注解 | @Test | @Test |
| 測試套件在執行之前需要執行的 | – | @BeforeSuite |
| 測試套件在執行之后需要執行的 | – | @AfterSuite |
| 在測試之前需要執行的 | – | @BeforeTest |
| 在測試之后需要執行的 | – | @AfterTest |
| 在一個測試方法所屬于的任意一個組的第一個方法被調用之前執行 | – | @BeforeGroups |
| 在一個測試方法所屬于的任意一個組的最后一個方法被調用之后執行 | – | @AfterGroups |
| 在當前類的第一個測試方法調用之前執行 | @BeforeClass | @BeforeClass |
| 在當前類的最后一個測試方法調用之后執行 | @AfterClass | @AfterClass |
| 每個測試方法之前需要執行 | @Before | @BeforeMethod |
| 每個測試方法之后需要執行 | @After | @AfterMethod |
| 忽略 | @ignore | @Test(enbale=false) |
| 預期異常 | @Test(expected = ArithmeticException.class) | @Test(expectedExceptions = ArithmeticException.class) |
| 超時 | @Test(timeout = 1000) | @Test(timeout = 1000) |
JUnit 4 和 TestNG 之間注解方面的區別主要有以下幾點:
- 在Junit 4 中,如果我們需要在方法前面使用@BeforeClass和@AfterClass,那么該測試方法則必須是靜態方法。TestNG 在方法定義部分則更加的靈活,它不需要類似的約束。
- 3個附加的setUp/tearDown級別:套件和分組(@Before/AfterSuite, @Before/AfterTest, @Before/AfterGroup)。想了解詳細的請看這里
JUnit 4
@BeforeClass
public static void oneTimeSetUp() {
// one-time initialization code
System.out.println("@BeforeClass - oneTimeSetUp");
} TestNG
@BeforeClass
public void oneTimeSetUp() {
// one-time initialization code
System.out.println("@BeforeClass - oneTimeSetUp");
} 在Junit 4中,注解的命名是比較令人困惑的,例如Before,AfterandExpected,我們不是很確切的能理解在方法前面有Before和After這樣的注解是做什么的,同樣Expected也如此。TestNG在這方面做的就好很多,注解使用了BeforeMethod,AfterMethod和ExpectedException,這樣的名字就非常好理解了。
異常測試
異常測試的意思是在單元測試中應該拋出什么異常是合理的,這個特性在兩個框架都已經實現。
JUnit 4
@Test(expected = ArithmeticException.class)
public void divisionWithException() {
int i = 1/0;
} TestNG
@Test(expectedExceptions = ArithmeticException.class)
public void divisionWithException() {
int i = 1/0;
} 忽略測試
忽略測試意思是在單元測試哪些是可以被忽略的,這個特性在兩個框架都已經實現。
JUnit 4
@Ignore("Not Ready to Run")
@Test
public void divisionWithException() {
System.out.println("Method is not ready yet");
} TestNG
@Test(enabled=false)
public void divisionWithException() {
System.out.println("Method is not ready yet");
} 時間測試
時間測試意思是如果一個單元測試運行的時間超過了一個指定的毫秒數,那么測試將終止并且標記為失敗的測試,這個特性在兩個框架都已經實現。
JUnit 4
@Test(timeout = 1000)
public void infinity() {
while (true);
} TestNG
@Test(timeOut = 1000)
public void infinity() {
while (true);
} 套件測試
套件測試就是把幾個單元測試組合成一個模塊,然后運行,這個特性兩個框架均已實現。然而卻是用了兩個不同的方式來實現的。
JUnit 4
@RunWith和@Suite注解被用于執行套件測試。下面的代碼是所展示的是在JunitTest5被執行之后需要JunitTest1和JunitTest2也一起執行。所有的聲明需要在類內部完成。
@RunWith(Suite.class)
@Suite.SuiteClasses({
JunitTest1.class,
JunitTest2.class
})
public class JunitTest5 {
} TestNG
執行套件測試是使用XML文件配置的方式來做。下面的 XML 的文件可以使得TestNGTest1和TestNGTest2一起執行。
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
<test name="testing">
<classes>
<class name="com.fsecure.demo.testng.TestNGTest1" />
<class name="com.fsecure.demo.testng.TestNGTest2" />
</classes>
</test>
</suite> TestNG可以在這塊做的更好,使用了組的概念,每個方法都可以被分配到一個組里面,可以根據功能特性來分組。例如:
這是一個有4個方法,3個組(method1, method2 和 method4)的類
@Test(groups="method1")
public void testingMethod1() {
System.out.println("Method - testingMethod1()");
}
@Test(groups="method2")
public void testingMethod2() {
System.out.println("Method - testingMethod2()");
}
@Test(groups="method1")
public void testingMethod1_1() {
System.out.println("Method - testingMethod1_1()");
}
@Test(groups="method4")
public void testingMethod4() {
System.out.println("Method - testingMethod4()");
} 下面XML文件定義了一個只是執行methed1的組的單元測試
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
<test name="testing">
<groups>
<run>
<include name="method1"/>
</run>
</groups>
<classes>
<class name="com.fsecure.demo.testng.TestNGTest5_2_0" />
</classes>
</test>
</suite> 使用分組的概念,集成測試就會更加強大。例如,我們可以只是執行所有測試中的組名為DatabaseFuntion的測試。
參數化測試
參數化測試意思是給單元測試傳多個參數值。這個特性在JUnit 4 和TestNG。然后兩個框架實現的方式卻完全不同。
JUnit 4
@RunWith和@Parameter注解用于為單元測試提供參數值,@Parameters必須返回 List,參數將會被作為參數傳給類的構造函數。
@RunWith(value = Parameterized.class)
public class JunitTest6 {
private int number;
public JunitTest6(int number) {
this.number = number;
}
@Parameters
public static Collection<Object[]> data() {
Object[][] data = new Object[][] { { 1 }, { 2 }, { 3 }, { 4 } };
return Arrays.asList(data);
}
@Test
public void pushTest() {
System.out.println("Parameterized Number is : " + number);
}
} 它在使用上有許多的限制;我們必須遵循 JUnit 的方式去聲明參數,參數必須通過構造函數的參數去初始化類的成員來用于測試。返回的參數類型必須是List [],數據已經被限定為String或者是一個原始值。
TestNG
使用XML文件或者@DataProvider注解來給測試提供參數。
XML文件配置參數化測試
只是在方法上聲明@Parameters注解,參數的數據將由 TestNG 的 XML 配置文件提供。這樣做之后,我們可以使用不同的數據集甚至是不同的結果集來重用一個測試用例。另外,甚至是最終用戶,QA 或者 QE 可以提供使用 XML 文件來提供他們自己的數據來做測試。
Unit Test
public class TestNGTest6_1_0 {
@Test
@Parameters(value="number")
public void parameterIntTest(int number) {
System.out.println("Parameterized Number is : " + number);
}
} XML 文件
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
<suite name="My test suite">
<test name="testing">
<parameter name="number" value="2"/>
<classes>
<class name="com.fsecure.demo.testng.TestNGTest6_0" />
</classes>
</test>
</suite> @DataProvider注解做參數化測試
使用XML文件初始化數據可以很方便,但是測試偶爾需要復雜的類型,一個String或原始值并不能完全滿足。 TestNG 的@ DataProvider的注解,可以更好的把復雜的參數類型映射到一個測試方法來處理這種情況。
@DataProvider可以使用 Vector, String 或者 Integer 類型的值作為參數
@Test(dataProvider = "Data-Provider-Function")
public void parameterIntTest(Class clzz, String[] number) {
System.out.println("Parameterized Number is : " + number[0]);
System.out.println("Parameterized Number is : " + number[1]);
}
//This function will provide the patameter data
@DataProvider(name = "Data-Provider-Function")
public Object[][] parameterIntTestProvider() {
return new Object[][]{
{Vector.class, new String[] {"java.util.AbstractList", "java.util.AbstractCollection"}},
{String.class, new String[] {"1", "2"}},
{Integer.class, new String[] {"1", "2"}}
};
} @DataProvider作為對象的參數
P.S “TestNGTest6_3_0” 是一個簡單的對象,使用了get和set方法。
@Test(dataProvider = "Data-Provider-Function")
public void parameterIntTest(TestNGTest6_3_0 clzz) {
System.out.println("Parameterized Number is : " + clzz.getMsg());
System.out.println("Parameterized Number is : " + clzz.getNumber());
}
//This function will provide the patameter data
@DataProvider(name = "Data-Provider-Function")
public Object[][] parameterIntTestProvider() {
TestNGTest6_3_0 obj = new TestNGTest6_3_0();
obj.setMsg("Hello");
obj.setNumber(123);
return new Object[][]{
{obj}
};
} TestNG的參數化測試使用起來非常的友好和靈活 (不管是XML配置還是在類里面注解的方式). 它可以使用許多復雜的數據類型作為參數的值,并且沒有什么限制。如上面的例子所示, we even can pass in our own object (TestNGTest6_3_0) for parameterized test
依賴測試
參數化測試意味著測試的方法是有依賴的,也就是要執行的的方法在執行之前需要執行的部分。如果依賴的方法出現錯誤,所有的子測試都會被忽略,不會被標記為失敗。
JUnit 4
JUnit 框架主要聚焦于測試的隔離,暫時還不支持這個特性。
TestNG
它使用dependOnMethods來實現了依賴測試的功能,如下:
@Test
public void method1() {
System.out.println("This is method 1");
}
@Test(dependsOnMethods={"method1"})
public void method2() {
System.out.println("This is method 2");
} 如果method1()成功執行,那么method2()也將被執行,否則method2()將會被忽略。
討論總結
當我們做完所有特性的對比以后,我建議使用 TestNG 作為 Java 項目的主要單元測試框架,因為 TestNG 在參數化測試、依賴測試以及套件測試(組)方面功能更加強大。TestNG 意味著高級的測試和復雜的集成測試。它更加的靈活,特別是對大的套件測試。另外,TestNG 也涵蓋了 JUnit4 的全部功能。那就沒有任何理由使用 Junit了。
參考資料
TestNG
————
http://en.wikipedia.org/wiki/TestNG
http://www.ibm.com/developerworks/java/library/j-testng/
http://testng.org/doc/index.html
JUnit
———–
http://en.wikipedia.org/wiki/JUnit
http://www.ibm.com/developerworks/java/library/j-junit4.html
http://junit.sourceforge.net/doc/faq/faq.htm
http://www.devx.com/Java/Article/31983/0/page/3
http://ourcraft.wordpress.com/2008/08/27/writing-a-parameterized-junit-test/
TestNG VS JUnit
——————
http://docs.codehaus.org/display/XPR/Migration+to+JUnit4+or+TestNG
http://www.ibm.com/developerworks/java/library/j-cq08296/index.html