<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 追求代碼質量: JUnit 4 與 TestNG 的對比 _為什么 TestNG 框架依然是大規模測試的較好選擇?_ JUnit 4 具有基于注釋的新框架,它包含了 TestNG 一些最優異的特性。但這是否意味著 JUnit 4 已經淘汰了 TestNG?Andrew Glover 探討了這兩種框架各自的獨特之處,并闡述了 TestNG 獨有的三種高級測試特性。 經過長時間積極的開發之后,JUnit 4.0 于今年年初發布了。JUnit 框架的某些最有趣的更改 —— 特別是對于本專欄的讀者來說 —— 正是通過巧妙地使用注釋實現的。除外觀和風格方面的顯著改進外,新框架的特性使測試用例的編制從結構規則中解放出來。使原來僵化的 fixture 模型更為靈活,有利于采取可配置程度更高的方法。因此,JUnit 框架不再強求把每一項測試工作定義為一個名稱以 `test` 開始的方法,并且現在可以只運行一次 fixture,而不是每次測試都需要運行一次。 雖然這些改變令人欣慰,但 JUnit 4 并不是第一個提供基于注釋的靈活模型的 Java? 測試框架。在修改 JUnit 之前很久,TestNG 就已建立為一個基于注釋的框架。 事實上,是 TestNG 在 Java 編程中_率先_ 實現了利用注釋進行測試,這使它成為 JUnit 的有力競爭對手。然而,自從 JUnit 4 發布后,很多開發者質疑:二者之間還有什么差別嗎?在本月的專欄中,我將討論 TestNG 不同于 JUnit 4 的一些特性,并提議采用一些方法,使得這兩個框架能繼續互相補充,而不是互相競爭。 ## 您知道嗎? 在 Ant 中運行 JUnit 4 測試比預計的要難得多。事實上,一些團隊已發現,惟一的解決方法是升級到 Ant 1.7。 ## 表面上的相似 JUnit 4 和 TestNG 有一些共同的重要特性。這兩個框架都讓測試工作簡單得令人吃驚(和愉快),給測試工作帶來了便利。二者也都擁有活躍的社區,為主動開發提供支持,同時生成豐富的文檔。 ## 提高代碼質量 要找到您最迫切問題的答案,請不要錯過 Andrew 的 [論壇](http://www.ibm.com/developerworks/forums/dw_forum.jsp?S_TACT=105AGX52&cat=10&S_CMP=cn-a-j&forum=812)。 兩個框架的不同在于核心設計。JUnit _一直_ 是一個單元測試框架,也就是說,其構建目的是促進單個對象的測試,它確實能夠極其有效地完成此類任務。而 TestNG 則是用來解決_更高_ 級別的測試問題,因此,它具有 JUnit 中所沒有的一些特性。 ### 一個簡單的測試用例 初看起來,JUnit 4 和 TestNG 中實現的測試非常相似。為了更好地理解我的意思,請看一下清單 1 中的代碼。這是一個 JUnit 4 測試,它有一個 macro-fixture(即僅在所有測試運行前調用一次的 fixture),這個 macro-fixture 由 `@BeforeClass` 屬性表示: ##### 清單 1\. 一個簡單的 JUnit 4 測試用例 ``` package test.com.acme.dona.dep; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.junit.BeforeClass; import org.junit.Test; public class DependencyFinderTest { private static DependencyFinder finder; @BeforeClass public static void init() throws Exception { finder = new DependencyFinder(); } @Test public void verifyDependencies() throws Exception { String targetClss = "test.com.acme.dona.dep.DependencyFind"; Filter[] filtr = new Filter[] { new RegexPackageFilter("java|junit|org")}; Dependency[] deps = finder.findDependencies(targetClss, filtr); assertNotNull("deps was null", deps); assertEquals("should be 5 large", 5, deps.length); } } ``` JUnit 用戶會立即注意到:這個類中沒有了以前版本的 JUnit 中所要求的一些_語法成分_。這個類沒有 `setUp()` 方法,也不對 `TestCase` 類進行擴展,甚至也沒有哪個方法的名稱以 `test` 開始。這個類還利用了 Java 5 的一些特性,例如靜態導入,很明顯地,它還使用了注釋。 ### 更多的靈活性 在清單 2 中,您可以看到_同一個_ 測試項目。不過這次是用 TestNG 實現的。這里的代碼跟清單 1 中的測試代碼有個微妙的差別。發現了嗎? ##### 清單 2\. 一個 TestNG 測試用例 ``` package test.com.acme.dona.dep; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import org.testng.annotations.BeforeClass; import org.testng.annotations.Configuration; import org.testng.annotations.Test; public class DependencyFinderTest { private DependencyFinder finder; @BeforeClass private void init(){ this.finder = new DependencyFinder(); } @Test public void verifyDependencies() throws Exception { String targetClss = "test.com.acme.dona.dep.DependencyFind"; Filter[] filtr = new Filter[] { new RegexPackageFilter("java|junit|org")}; Dependency[] deps = finder.findDependencies(targetClss, filtr); assertNotNull(deps, "deps was null" ); assertEquals(5, deps.length, "should be 5 large"); } } ``` 顯然,這兩個清單很相似。不過,如果仔細看,您會發現 TestNG 的編碼規則比 JUnit 4 更靈活。[清單 1](#code1) 里,在 JUnit 中我必須把 `@BeforeClass` 修飾的方法聲明為 `static`,這又要求我把 fixture,即 `finder` 聲明為 `static`。我還必須把 `init()` 聲明為 `public`。看看清單 2,您就會發現不同。這里不再需要那些規則了。我的 `init()` 方法既不是 `static`,也不是 `public`。 從最初起,TestNG 的靈活性就是其主要優勢之一,但這并非它惟一的賣點。TestNG 還提供了 JUnit 4 所不具備的其他一些特性。 * * * ## 依賴性測試 JUnit 框架想達到的一個目標就是測試隔離。它的缺點是:人們很難確定測試用例執行的順序,而這對于任何類型的依賴性測試都非常重要。開發者們使用了多種技術來解決這個問題,例如,按字母順序指定測試用例,或是更多地依靠 fixture 來適當地解決問題。 如果測試成功,這些解決方法都沒什么問題。但是,如果測試不成功,就會產生一個很麻煩的后果:_所有_ 后續的依賴測試也會失敗。在某些情況下,這會使大型測試套件報告出許多不必要的錯誤。例如,假設有一個測試套件測試一個需要登錄的 Web 應用程序。您可以創建一個有依賴關系的方法,通過登錄到這個應用程序來創建整個測試套件,從而避免 JUnit 的隔離機制。這種解決方法不錯,但是如果登錄失敗,即使登錄該應用程序后的其他功能都正常工作,整個測試套件依然會全部失敗! ### 跳過,而不是標為失敗 與 JUnit 不同,TestNG 利用 `Test` 注釋的 `dependsOnMethods` 屬性來應對測試的依賴性問題。有了這個便利的特性,就可以輕松指定依賴方法。例如,前面所說的登錄將在某個方法_之前_ 運行。此外,如果依賴方法失敗,它將被_跳過_,而不是標記為失敗。 ##### 清單 3\. 使用 TestNG 進行依賴性測試 ``` import net.sourceforge.jwebunit.WebTester; public class AccountHistoryTest { private WebTester tester; @BeforeClass protected void init() throws Exception { this.tester = new WebTester(); this.tester.getTestContext(). setBaseUrl("http://div.acme.com:8185/ceg/"); } @Test public void verifyLogIn() { this.tester.beginAt("/"); this.tester.setFormElement("username", "admin"); this.tester.setFormElement("password", "admin"); this.tester.submit(); this.tester.assertTextPresent("Logged in as admin"); } @Test (dependsOnMethods = {"verifyLogIn"}) public void verifyAccountInfo() { this.tester.clickLinkWithText("History", 0); this.tester.assertTextPresent("GTG Data Feed"); } } ``` 在清單 3 中定義了兩個測試:一個驗證登錄,另一個驗證賬戶信息。請注意,通過使用 `Test` 注釋的 `dependsOnMethods = {"verifyLogIn"}` 子句,`verifyAccountInfo` 測試指定了它依賴 `verifyLogIn()` 方法。 通過 TestNG 的 Eclipse 插件(例如)運行該測試時,如果 `verifyLogIn` 測試失敗,TestNG 將直接跳過 `verifyAccountInfo` 測試,請參見圖 1: ##### 圖 1\. 在 TestNG 中跳過的測試 ![](https://box.kancloud.cn/2016-05-11_5732e0d718dd1.jpg) 對于大型測試套件,TestNG 這種不標記為失敗,而只是跳過的處理方法可以減輕很多壓力。您的團隊可以集中精力查找為什么百分之五十的測試套件被跳過,而不是去找百分之五十的測試套件失敗的原因!更有利的是,TestNG 采取了只重新運行失敗測試的機制,這使它的依賴性測試設置更為完善。 * * * ## 失敗和重運行 在大型測試套件中,這種重新運行失敗測試的能力顯得尤為方便。這是 TestNG 獨有的一個特性。在 JUnit 4 中,如果測試套件包括 1000 項測試,其中 3 項失敗,很可能就會迫使您重新運行整個測試套件(修改錯誤以后)。不用說,這樣的工作可能會耗費幾個小時。 一旦 TestNG 中出現失敗,它就會創建一個 XML 配置文件,對失敗的測試加以說明。如果利用這個文件執行 TestNG 運行程序,TestNG 就_只_ 運行失敗的測試。所以,在前面的例子里,您只需重新運行那三個失敗的測試,而不是整個測試套件。 實際上,您可以通過清單 2 中的 Web 測試的例子自己看到這點。`verifyLogIn()` 方法失敗時,TestNG 自動創建一個 testng-failed.xml 文件。該文件將成為如清單 4 所示的替代性測試套件: ##### 清單 4\. 失敗測試的 XML 文件 ``` <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite thread-count="5" verbose="1" name="Failed suite [HistoryTesting]" parallel="false" annotations="JDK5"> <test name="test.com.acme.ceg.AccountHistoryTest(failed)" junit="false"> <classes> <class name="test.com.acme.ceg.AccountHistoryTest"> <methods> <include name="verifyLogIn"/> </methods> </class> </classes> </test> </suite> ``` 運行小的測試套件時,這個特性似乎沒什么大不了。但是如果您的測試套件規模較大,您很快就會體會到它的好處。 * * * ## 參數化測試 TestNG 中另一個有趣的特性是_參數化測試_。在 JUnit 中,如果您想改變某個受測方法的參數組,就只能給_每個_ 不同的參數組編寫一個測試用例。多數情況下,這不會帶來太多麻煩。然而,我們有時會碰到一些情況,對其中的業務邏輯,需要運行的測試數目變化范圍很大。 在這樣的情況下,使用 JUnit 的測試人員往往會轉而使用 FIT 這樣的框架,因為這樣就可以用表格數據驅動測試。但是 TestNG 提供了開箱即用的類似特性。通過在 TestNG 的 XML 配置文件中放入參數化數據,就可以對不同的數據集重用同一個測試用例,甚至有可能會得到不同的結果。這種技術完美地避免了_只能_ 假定一切正常的測試,或是沒有對邊界進行有效驗證的情況。 在清單 5 中,我用 Java 1.4 定義了一個 TestNG 測試,該測試可接收兩個參數:`classname` 和 `size`。這兩個參數可以驗證某個類的層次結構(也就是說,如果傳入 `java.util.Vector`,則 `HierarchyBuilder` 所構建的 `Hierarchy` 的值將為 `2` )。 ##### 清單 5\. 一個 TestNG 參數化測試 ``` package test.com.acme.da; import com.acme.da.hierarchy.Hierarchy; import com.acme.da.hierarchy.HierarchyBuilder; public class HierarchyTest { /** * @testng.test * @testng.parameters value="class_name, size" */ public void assertValues(String classname, int size) throws Exception{ Hierarchy hier = HierarchyBuilder.buildHierarchy(classname); assert hier.getHierarchyClassNames().length == size: "didn't equal!"; } } ``` 清單 5 列出了一個泛型測試,它可以采用不同的數據反復重用。請花點時間思考一下這個問題。如果有 10 個不同的參數組合需要在 JUnit 中測試,您只能寫 10 個測試用例。每個測試用例完成的任務基本是相同的,只是受測方法的參數有所改變。但是,如果使用參數化測試,就可以只定義_一個_ 測試用例,然后,(舉例來說)把所需的參數模式加到 TestNG 的測試套件文件中。清單 6 中展示了這中方法: ##### 清單 6\. 一個 TestNG 參數化測試套件文件 ``` <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd"> <suite name="Deckt-10"> <test name="Deckt-10-test"> <parameter name="class_name" value="java.util.Vector"/> <parameter name="size" value="2"/> <classes> <class name="test.com.acme.da.HierarchyTest"/> </classes> </test> </suite> ``` 清單 6 中的 TestNG 測試套件文件只對該測試定義了一個參數組(`class_name` 為 `java.util.Vector`,且 `size` 等于 `2`),但卻具有無限的可能。這樣做的一個額外的好處是:將測試數據移動到 XML 文件的無代碼工件就意味著非程序員也可以指定數據。 * * * ## 高級參數化測試 盡管從一個 XML 文件中抽取數據會很方便,但偶爾會有些測試需要有復雜類型,這些類型無法用 `String` 或原語值來表示。TestNG 可以通過它的 `@DataProvider` 注釋處理這樣的情況。`@DataProvider` 注釋可以方便地把復雜參數類型映射到某個測試方法。例如,清單 7 中的 `verifyHierarchy` 測試中,我采用了重載的 `buildHierarchy` 方法,它可接收一個 `Class` 類型的數據, 它斷言(asserting)`Hierarchy` 的 `getHierarchyClassNames()` 方法應該返回一個適當的字符串數組: ##### 清單 7\. TestNG 中的 DataProvider 用法 ``` package test.com.acme.da.ng; import java.util.Vector; import static org.testng.Assert.assertEquals; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import com.acme.da.hierarchy.Hierarchy; import com.acme.da.hierarchy.HierarchyBuilder; public class HierarchyTest { @DataProvider(name = "class-hierarchies") public Object[][] dataValues(){ return new Object[][]{ {Vector.class, new String[] {"java.util.AbstractList", "java.util.AbstractCollection"}}, {String.class, new String[] {}} }; } @Test(dataProvider = "class-hierarchies") public void verifyHierarchy(Class clzz, String[] names) throws Exception{ Hierarchy hier = HierarchyBuilder.buildHierarchy(clzz); assertEquals(hier.getHierarchyClassNames(), names, "values were not equal"); } } ``` `dataValues()` 方法通過一個多維數組提供與 `verifyHierarchy` 測試方法的參數值匹配的數據值。TestNG 遍歷這些數據值,并根據數據值調用了兩次 `verifyHierarchy`。在第一次調用時,`Class` 參數被設置為 `Vector.class` ,而 `String` 數組參數容納 “`java.util.AbstractList` ” 和 “ `java.util.AbstractCollection` ” 這兩個 `String` 類型的數據。這樣挺方便吧? * * * ## 為什么只選擇其一? 我已經探討了對我而言,TestNG 的一些獨有優勢,但是它還有其他幾個特性是 JUnit 所不具備的。例如 TestNG 中使用了測試分組,它可以根據諸如運行時間這樣的特征來對測試分類。也可在 Java 1.4 中通過 javadoc 風格的注釋來使用它,如 [清單 5](#code5) 所示。 正如我在本文開頭所說,JUnit 4 和 TestNG 在表面上是相似的。然而,設計 JUnit 的目的是為了分析代碼單元,而 TestNG 的預期用途則針對高級測試。對于大型測試套件,我們不希望在某一項測試失敗時就得重新運行數千項測試,TestNG 的靈活性在這里尤為有用。這兩個框架都有自己的優勢,您可以隨意同時使用它們。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看