<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之旅 廣告
                <!-- Annotation-Based Unit Testing --> ## 基于注解的單元測試 單元測試是對類中每個方法提供一個或者多個測試的一種事件,其目的是為了有規律的測試一個類中每個部分是否具備正確的行為。在 Java 中,最著名的單元測試工具就是 **JUnit**。**JUnit** 4 版本已經包含了注解。在注解版本之前的 JUnit 一個最主要的問題是,為了啟動和運行 **JUnit** 測試,有大量的“儀式”需要標注。這種負擔已經減輕了一些,**但是**注解使得測試更接近“可以工作的最簡單的測試系統”。 在注解版本之前的 JUnit,你必須創建一個單獨的文件來保存單元測試。通過注解,我們可以將單元測試集成在需要被測試的類中,從而將單元測試的時間和麻煩降到了最低。這種方式有額外的好處,就是使得測試私有方法和公有方法變的一樣容易。 這個基于注解的測試框架叫做 **@Unit**。其最基本的測試形式,可能也是你使用的最多的一個注解是 **@Test**,我們使用 **@Test** 來標記測試方法。測試方法不帶參數,并返回 **boolean** 結果來說明測試方法成功或者失敗。你可以任意命名它的測試方法。同時 **@Unit** 測試方法可以是任意你喜歡的訪問修飾方法,包括 **private**。 要使用 **@Unit**,你必須導入 **onjava.atunit** 包,并且使用 **@Unit** 的測試標記為合適的方法和字段打上標簽(在接下來的例子中你會學到),然后讓你的構建系統對編譯后的類運行 **@Unit**,下面是一個簡單的例子: ```java // annotations/AtUnitExample1.java // {java onjava.atunit.AtUnit // build/classes/main/annotations/AtUnitExample1.class} package annotations; import onjava.atunit.*; import onjava.*; public class AtUnitExample1 { public String methodOne() { return "This is methodOne"; } public int methodTwo() { System.out.println("This is methodTwo"); return 2; } @Test boolean methodOneTest() { return methodOne().equals("This is methodOne"); } @Test boolean m2() { return methodTwo() == 2; } @Test private boolean m3() { return true; } // Shows output for failure: @Test boolean failureTest() { return false; } @Test boolean anotherDisappointment() { return false; } } ``` 輸出為: ```java annotations.AtUnitExample1 . m3 . methodOneTest . m2 This is methodTwo . failureTest (failed) . anotherDisappointment (failed) (5 tests) >>> 2 FAILURES <<< annotations.AtUnitExample1: failureTest annotations.AtUnitExample1: anotherDisappointment ``` 使用 **@Unit** 進行測試的類必須定義在某個包中(即必須包括 **package** 聲明)。 **@Test** 注解被置于 `methodOneTest()`、 `m2()`、`m3()`、`failureTest()` 以及 `anotherDisappointment()` 方法之前,它們告訴 **@Unit** 方法作為單元測試來運行。同時 **@Test** 確保這些方法沒有任何參數并且返回值為 **boolean** 或者 **void**。當你填寫單元測試時,唯一需要做的就是決定測試是成功還是失敗,(對于返回值為 **boolean** 的方法)應該返回 **ture** 還是 **false**。 如果你熟悉 **JUnit**,你還將注意到 **@Unit** 輸出的信息更多。你會看到現在正在運行的測試的輸出更有用,最后它會告訴你導致失敗的類和測試。 你并非必須將測試方法嵌入到原來的類中,有時候這種事情根本做不到。要生產一個非嵌入式的測試,最簡單的方式就是繼承: ```java // annotations/AUExternalTest.java // Creating non-embedded tests // {java onjava.atunit.AtUnit // build/classes/main/annotations/AUExternalTest.class} package annotations; import onjava.atunit.*; import onjava.*; public class AUExternalTest extends AtUnitExample1 { @Test boolean _MethodOne() { return methodOne().equals("This is methodOne"); } @Test boolean _MethodTwo() { return methodTwo() == 2; } } ``` 輸出為: ```java annotations.AUExternalTest . tMethodOne . tMethodTwo This is methodTwo OK (2 tests) ``` 這個示例還表現出靈活命名的價值。在這里,**@Test** 方法被命名為下劃線前綴加上要測試的方法名稱(我并不認為這是一種理想的命名形式,這只是表現一種可能性罷了)。 你也可以使用組合來創建非嵌入式的測試: ```java // annotations/AUComposition.java // Creating non-embedded tests // {java onjava.atunit.AtUnit // build/classes/main/annotations/AUComposition.class} package annotations; import onjava.atunit.*; import onjava.*; public class AUComposition { AtUnitExample1 testObject = new AtUnitExample1(); @Test boolean tMethodOne() { return testObject.methodOne() .equals("This is methodOne"); } @Test boolean tMethodTwo() { return testObject.methodTwo() == 2; } } ``` 輸出為: ```java annotations.AUComposition . tMethodTwo This is methodTwo . tMethodOne OK (2 tests) ``` 因為在每一個測試里面都會創建 **AUComposition** 對象,所以創建新的成員變量 **testObject** 用于以后的每一個測試方法。 因為 **@Unit** 中沒有 **JUnit** 中特殊的 **assert** 方法,不過另一種形式的 **@Test** 方法仍然允許返回值為 **void**(如果你還想使用 **true** 或者 **false** 的話,也可以使用 **boolean** 作為方法返回值類型)。為了表示測試成功,可以使用 Java 的 **assert** 語句。Java 斷言機制需要你在 java 命令行行加上 **-ea** 標志來開啟,但是 **@Unit** 已經自動開啟了該功能。要表示測試失敗的話,你甚至可以使用異常。**@Unit** 的設計目標之一就是盡可能減少添加額外的語法,而 Java 的 **assert** 和異常對于報告錯誤而言,即已經足夠了。一個失敗的 **assert** 或者從方法從拋出的異常都被視為測試失敗,但是 **@Unit** 不會在這個失敗的測試上卡住,它會繼續運行,直到所有測試完畢,下面是一個示例程序: ```java // annotations/AtUnitExample2.java // Assertions and exceptions can be used in @Tests // {java onjava.atunit.AtUnit // build/classes/main/annotations/AtUnitExample2.class} package annotations; import java.io.*; import onjava.atunit.*; import onjava.*; public class AtUnitExample2 { public String methodOne() { return "This is methodOne"; } public int methodTwo() { System.out.println("This is methodTwo"); return 2; } @Test void assertExample() { assert methodOne().equals("This is methodOne"); } @Test void assertFailureExample() { assert 1 == 2: "What a surprise!"; } @Test void exceptionExample() throws IOException { try(FileInputStream fis = new FileInputStream("nofile.txt")) {} // Throws } @Test boolean assertAndReturn() { // Assertion with message: assert methodTwo() == 2: "methodTwo must equal 2"; return methodOne().equals("This is methodOne"); } } ``` 輸出為: ```java annotations.AtUnitExample2 . exceptionExample java.io.FileNotFoundException: nofile.txt (The system cannot find the file specified) (failed) . assertExample . assertAndReturn This is methodTwo . assertFailureExample java.lang.AssertionError: What a surprise! (failed) (4 tests) >>> 2 FAILURES <<< annotations.AtUnitExample2: exceptionExample annotations.AtUnitExample2: assertFailureExample ``` 如下是一個使用非嵌入式測試的例子,并且使用了斷言,它將會對 **java.util.HashSet** 進行一些簡單的測試: ```java // annotations/HashSetTest.java // {java onjava.atunit.AtUnit // build/classes/main/annotations/HashSetTest.class} package annotations; import java.util.*; import onjava.atunit.*; import onjava.*; public class HashSetTest { HashSet<String> testObject = new HashSet<>(); @Test void initialization() { assert testObject.isEmpty(); } @Test void _Contains() { testObject.add("one"); assert testObject.contains("one"); } @Test void _Remove() { testObject.add("one"); testObject.remove("one"); assert testObject.isEmpty(); } } ``` 采用繼承的方式可能會更簡單,也沒有一些其他的約束。 對每一個單元測試而言,**@Unit** 都會使用默認的無參構造器,為該測試類所屬的類創建出一個新的實例。并在此新創建的對象上運行測試,然后丟棄該對象,以免對其他測試產生副作用。如此創建對象導致我們依賴于類的默認構造器。如果你的類沒有默認構造器,或者對象需要復雜的構造過程,那么你可以創建一個 **static** 方法專門負責構造對象,然后使用 **@TestObjectCreate** 注解標記該方法,例子如下: ```java // annotations/AtUnitExample3.java // {java onjava.atunit.AtUnit // build/classes/main/annotations/AtUnitExample3.class} package annotations; import onjava.atunit.*; import onjava.*; public class AtUnitExample3 { private int n; public AtUnitExample3(int n) { this.n = n; } public int getN() { return n; } public String methodOne() { return "This is methodOne"; } public int methodTwo() { System.out.println("This is methodTwo"); return 2; } @TestObjectCreate static AtUnitExample3 create() { return new AtUnitExample3(47); } @Test boolean initialization() { return n == 47; } @Test boolean methodOneTest() { return methodOne().equals("This is methodOne"); } @Test boolean m2() { return methodTwo() == 2; } } ``` 輸出為: ```java annotations.AtUnitExample3 . initialization . m2 This is methodTwo . methodOneTest OK (3 tests) ``` **@TestObjectCreate** 修飾的方法必須聲明為 **static** ,且必須返回一個你正在測試的類型對象,這一切都由 **@Unit** 負責確保成立。 有的時候,你需要向單元測試中增加一些字段。這時候可以使用 **@TestProperty** 注解,由它注解的字段表示只在單元測試中使用(因此,在你將產品發布給客戶之前,他們應該被刪除)。在下面的例子中,一個 **String** 通過 `String.split()` 方法進行分割,從其中讀取一個值,這個值將會被生成測試對象: ```java // annotations/AtUnitExample4.java // {java onjava.atunit.AtUnit // build/classes/main/annotations/AtUnitExample4.class} // {VisuallyInspectOutput} package annotations; import java.util.*; import onjava.atunit.*; import onjava.*; public class AtUnitExample4 { static String theory = "All brontosauruses " + "are thin at one end, much MUCH thicker in the " + "middle, and then thin again at the far end."; private String word; private Random rand = new Random(); // Time-based seed public AtUnitExample4(String word) { this.word = word; } public String getWord() { return word; } public String scrambleWord() { List<Character> chars = Arrays.asList( ConvertTo.boxed(word.toCharArray())); Collections.shuffle(chars, rand); StringBuilder result = new StringBuilder(); for(char ch : chars) result.append(ch); return result.toString(); } @TestProperty static List<String> input = Arrays.asList(theory.split(" ")); @TestProperty static Iterator<String> words = input.iterator(); @TestObjectCreate static AtUnitExample4 create() { if(words.hasNext()) return new AtUnitExample4(words.next()); else return null; } @Test boolean words() { System.out.println("'" + getWord() + "'"); return getWord().equals("are"); } @Test boolean scramble1() { // Use specific seed to get verifiable results: rand = new Random(47); System.out.println("'" + getWord() + "'"); String scrambled = scrambleWord(); System.out.println(scrambled); return scrambled.equals("lAl"); } @Test boolean scramble2() { rand = new Random(74); System.out.println("'" + getWord() + "'"); String scrambled = scrambleWord(); System.out.println(scrambled); return scrambled.equals("tsaeborornussu"); } } ``` 輸出為: ```java annotations.AtUnitExample4 . words 'All' (failed) . scramble1 'brontosauruses' ntsaueorosurbs (failed) . scramble2 'are' are (failed) (3 tests) >>> 3 FAILURES <<< annotations.AtUnitExample4: words annotations.AtUnitExample4: scramble1 annotations.AtUnitExample4: scramble2 ``` **@TestProperty** 也可以用來標記那些只在測試中使用的方法,但是它們本身不是測試方法。 如果你的測試對象需要執行某些初始化工作,并且使用完成之后還需要執行清理工作,那么可以選擇使用 **static** 的 **@TestObjectCleanup** 方法,當測試對象使用結束之后,該方法會為你執行清理工作。在下面的示例中,**@TestObjectCleanup** 為每一個測試對象都打開了一個文件,因此必須在丟棄測試的時候關閉該文件: ```java // annotations/AtUnitExample5.java // {java onjava.atunit.AtUnit // build/classes/main/annotations/AtUnitExample5.class} package annotations; import java.io.*; import onjava.atunit.*; import onjava.*; public class AtUnitExample5 { private String text; public AtUnitExample5(String text) { this.text = text; } @Override public String toString() { return text; } @TestProperty static PrintWriter output; @TestProperty static int counter; @TestObjectCreate static AtUnitExample5 create() { String id = Integer.toString(counter++); try { output = new PrintWriter("Test" + id + ".txt"); } catch(IOException e) { throw new RuntimeException(e); } return new AtUnitExample5(id); } @TestObjectCleanup static void cleanup(AtUnitExample5 tobj) { System.out.println("Running cleanup"); output.close(); } @Test boolean test1() { output.print("test1"); return true; } @Test boolean test2() { output.print("test2"); return true; } @Test boolean test3() { output.print("test3"); return true; } } ``` 輸出為: ```java annotations.AtUnitExample5 . test1 Running cleanup . test3 Running cleanup . test2 Running cleanup OK (3 tests) ``` 在輸出中我們可以看到,清理方法會在每個測試方法結束之后自動運行。
                  <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>

                              哎呀哎呀视频在线观看