<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          五年了,你還在用junit4嗎?

          共 16311字,需瀏覽 33分鐘

           ·

          2021-04-23 17:01

          junit5

          JUnit5在2017年就發(fā)布了,你還在用junit4嗎?

          什么是junit5

          與以前的JUnit版本不同,JUnit 5由三個不同子項(xiàng)目的多個不同模塊組成。

          JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

          JUnit Platform為在JVM上啟動測試框架提供基礎(chǔ)。它還定義了TestEngine API, 用來開發(fā)在平臺上運(yùn)行的測試框架。此外,平臺提供了一個控制臺啟動器],用于從命令行啟動平臺,并為Gradle和Maven提供構(gòu)建插件以[基于JUnit 4的Runner,用于在平臺上運(yùn)行任意TestEngine

          JUnit Jupiter是在JUnit 5中編寫測試和擴(kuò)展的新型編程模型和[擴(kuò)展模型][]的組合.Jupiter子項(xiàng)目提供了TestEngine,用于在平臺上運(yùn)行基于Jupiter的測試。

          JUnit Vintage提供TestEngine,用于在平臺上運(yùn)行基于JUnit 3和JUnit 4的測試。

          為什么需要 JUnit 5

          自從有了類似 JUnit 之類的測試框架,Java 單元測試領(lǐng)域逐漸成熟,開發(fā)人員對單元測試框架也有了更高的要求:更多的測試方式,更少的其他庫的依賴。

          因此,大家期待著一個更強(qiáng)大的測試框架誕生,JUnit 作為Java測試領(lǐng)域的領(lǐng)頭羊,推出了 JUnit 5 這個版本,主要特性:

          • 提供全新的斷言和測試注解,支持測試類內(nèi)嵌
          • 更豐富的測試方式:支持動態(tài)測試,重復(fù)測試,參數(shù)化測試等
          • 實(shí)現(xiàn)了模塊化,讓測試執(zhí)行和測試發(fā)現(xiàn)等不同模塊解耦,減少依賴
          • 提供對 Java 8 的支持,如 Lambda 表達(dá)式,Sream API等。

          基本注解

          @Test: 表示方法是測試方法。但是與JUnit4的@Test不同,他的職責(zé)非常單一不能聲明任何屬性,拓展的測試將會由Jupiter提供額外測試

          @ParameterizedTest: 表示方法是參數(shù)化測試

          @RepeatedTest: 表示方法可重復(fù)執(zhí)行

          @DisplayName: 為測試類或者測試方法設(shè)置展示名稱

          @BeforeEach: 表示在每個單元測試之前執(zhí)行

          @AfterEach: 表示在每個單元測試之后執(zhí)行

          @BeforeAll: 表示在所有單元測試之前執(zhí)行

          @AfterAll: 表示在所有單元測試之后執(zhí)行

          @Tag: 表示單元測試類別,類似于JUnit4中的@Categories

          @Disabled: 表示測試類或測試方法不執(zhí)行,類似于JUnit4中的@Ignore

          @Timeout: 表示測試方法運(yùn)行如果超過了指定時(shí)間將會返回錯誤

          @ExtendWith: 為測試類或測試方法提供擴(kuò)展類引用

          常用注解格式:

          class StandardTests {

              //與junit4的@beforeClass類似,每個測試類運(yùn)行一次
              @BeforeAll
              static void initAll() {
              }

              //與junit4中@before類似,每個測試用例都運(yùn)行一次
              @BeforeEach
              void init() {
              }

              @Test
              @DisplayName("成功測試")
              void succeedingTest() {
              }

              @Test
              @DisplayName("失敗測試")
              void failingTest() {
                  fail("a failing test");
              }

              //禁用測試用例
              @Test
              @Disabled("for demonstration purposes")
              void skippedTest() {
                  // not executed
              }

              @Test
              void abortedTest() {
                  assumeTrue("abc".contains("Z"));
                  fail("test should have been aborted");
              }


              //與@BeforeEach對應(yīng),每個測試類執(zhí)行一次,一般用于恢復(fù)環(huán)境
              @AfterEach
              void tearDown() {
              }

              //與@BeforeAll對應(yīng),每個測試類執(zhí)行一次,一般用于恢復(fù)環(huán)境
              @AfterAll
              static void tearDownAll() {
              }
          }

          新特性

          顯示名稱

          @DisplayName("顯示名稱測試")
          class DisplayNameDemo {

              @Test
              @DisplayName("我的 第一個 測試 用例")
              void testWithDisplayNameContainingSpaces() {
              }

              @Test
              @DisplayName("╯°□°)╯")
              void testWithDisplayNameContainingSpecialCharacters() {
              }

              @Test
              @DisplayName("??")
              void testWithDisplayNameContainingEmoji() {
              }
          }

          IDE運(yùn)行測試結(jié)果顯示:

          image-20210416232329161

          **優(yōu)點(diǎn):**通過這種方式,可以在方法名是英文特別長或者很難用英文描述清楚的場景下,增加中文解釋

          更強(qiáng)大的斷言

          JUnit Jupiter提供了許多JUnit4已有的斷言方法,并增加了一些適合與Java 8 lambda一起使用的斷言方法。所有JUnit Jupiter斷言都是[org.junit.jupiter.Assertions]類中的靜態(tài)方法。

          分組斷言:

          多個條件同時(shí)滿足時(shí)才斷言成功

          @Test
          void groupedAssertions() {
              Person person = new Person();

              Assertions.assertAll("person",
                                   () -> assertEquals("niu", person.getName()),
                                   () -> assertEquals(18, person.getAge())
                                  );
          }

          異常斷言:

          Junit4時(shí)需要使用rule方式,junit5提供了assertThrows更優(yōu)雅的異常斷言

          @Test
          void exceptionTesting() {
              Throwable exception = assertThrows(IllegalArgumentException.class, () -> {
                  throw new IllegalArgumentException("a message");
              });
              assertEquals("a message", exception.getMessage());
          }

          超時(shí)斷言:

          @Test
          @DisplayName("超時(shí)測試")
          public void timeoutTest() {
              Assertions.assertTimeout(Duration.ofMillis(100), () -> Thread.sleep(50));
          }

          標(biāo)簽和過濾

          通過標(biāo)簽把測試分組,在不同階段執(zhí)行不同的邏輯測試,比如劃分為快速冒煙測試和執(zhí)行慢但也重要的測試

          @Test
          @Tag("fast")
           void testing_faster() {
          }

          @Test
          @Tag("slow")
           void testing_slow() {
          }

          然后通過配置maven-surefire-plugin插件

          <plugin>
              <artifactId>maven-surefire-plugin</artifactId>
              <version>2.22.0</version>
              <configuration>
                  <properties>
                      <includeTags>fast</includeTags>
                      <excludeTages>slow</excludeTages>
                  </properties>
              </configuration>
          </plugin>

          嵌套測試

          當(dāng)我們編寫的類和代碼逐漸增多,隨之而來的需要測試的對應(yīng)測試類也會越來越多。

          為了解決測試類數(shù)量爆炸的問題,JUnit 5提供了@Nested 注解,能夠以靜態(tài)內(nèi)部成員類的形式對測試用例類進(jìn)行邏輯分組。

          并且每個靜態(tài)內(nèi)部類都可以有自己的生命周期方法, 這些方法將按從外到內(nèi)層次順序執(zhí)行。

          此外,嵌套的類也可以用@DisplayName 標(biāo)記,這樣我們就可以使用正確的測試名稱。下面看下簡單的用法:

          @DisplayName("A stack")
          class TestingAStackDemo {

              Stack<Object> stack;

              @Test
              @DisplayName("is instantiated with new Stack()")
              void isInstantiatedWithNew() {
                  new Stack<>();
              }

              @Nested
              @DisplayName("when new")
              class WhenNew {

                  @BeforeEach
                  void createNewStack() {
                      stack = new Stack<>();
                  }

                  @Test
                  @DisplayName("is empty")
                  void isEmpty() {
                      assertTrue(stack.isEmpty());
                  }


                  @Nested
                  @DisplayName("after pushing an element")
                  class AfterPushing {

                      String anElement = "an element";

                      @BeforeEach
                      void pushAnElement() {
                          stack.push(anElement);
                      }

                      @Test
                      @DisplayName("it is no longer empty")
                      void isNotEmpty() {
                          assertFalse(stack.isEmpty());
                      }
                  }
              }
          }

          junit沒有限制嵌套層數(shù),除非必要一般不建議使用超過3層,過于復(fù)雜的層次結(jié)構(gòu)會增加開發(fā)者理解用例關(guān)系的難度

          構(gòu)造函數(shù)和方法的依賴注入

          在之前的所有JUnit版本中,測試構(gòu)造函數(shù)或方法都不允許有參數(shù)(至少不能使用標(biāo)準(zhǔn)的Runner實(shí)現(xiàn))。作為JUnit Jupiter的主要變化之一,測試構(gòu)造函數(shù)和方法現(xiàn)在都允許有參數(shù)。這帶來了更大的靈活性,并為構(gòu)造函數(shù)和方法啟用依賴注入

          • TestInfo可獲取測試信息
          • TestReporter可以向控制臺輸出信息
          @Test
          @DisplayName("test-first")
          @Tag("my-tag")
          void test1(TestInfo testInfo) {
              assertEquals("test-first", testInfo.getDisplayName());
              assertTrue(testInfo.getTags().contains("my-tag"));
          }

          @Test
          @DisplayName("test-second")
          @Tag("my-tag")
          void test2(TestReporter testReporter) {
              testReporter.publishEntry("a key""a value");
          }

          重復(fù)測試

          多次調(diào)用同一個測試用例

          @RepeatedTest(10)
          @DisplayName("重復(fù)測試")
          public void testRepeated() {
              //...
          }
          image-20210416232512919

          動態(tài)測試

          動態(tài)測試只需要編寫一處代碼,就能一次性對各種類型的輸入和輸出結(jié)果進(jìn)行驗(yàn)證

          @TestFactory
          @DisplayName("動態(tài)測試")
          Stream<DynamicTest> dynamicTests() {
              List<Person> persons = getAllPerson();

              return persons.stream()
                  .map(person -> DynamicTest.dynamicTest(person.getName() + "-test", () -> assertTrue(person.getName().contains("niu"))));
          }

          超時(shí)測試

          通過時(shí)間來驗(yàn)證用例是否超時(shí),一般要求單個單元測試不應(yīng)該超過1秒

          class TimeoutDemo {
              @BeforeEach
              @Timeout(5)
              void setUp() {
                  // fails if execution time exceeds 5 seconds
              }

              @Test
              @Timeout(value = 1000, unit = TimeUnit.MILLISECONDS)
              void failsIfExecutionTimeExceeds1000Milliseconds() {
                  // fails if execution time exceeds 1000 milliseconds
                  //也可用這種方式 Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(1500));
              }
          }

          參數(shù)測試

          參數(shù)測試我覺得是最好用的特性,可以大量減少重復(fù)模板式代碼,也是junit5最驚艷的提升,強(qiáng)烈推薦使用

          @ValueSource: 為參數(shù)化測試指定入?yún)碓矗С职舜蠡A(chǔ)類以及String類型,Class類型

          @NullSource: 表示為參數(shù)化測試提供一個null的入?yún)?/p>

          @EnumSource: 表示為參數(shù)化測試提供一個枚舉入?yún)?/p>

          @CsvSource:表示讀取CSV格式內(nèi)容作為參數(shù)化測試入?yún)?/p>

          @CsvFileSource:表示讀取指定CSV文件內(nèi)容作為參數(shù)化測試入?yún)?/p>

          @MethodSource:表示讀取指定方法的返回值作為參數(shù)化測試入?yún)?注意方法返回需要是一個流)

          @ArgumentsSource:指定一個自定義的,可重用的ArgumentsProvider

          看完用法描述,簡直太喜歡了

          一個頂三個基礎(chǔ)測試用例

          @ParameterizedTest
          @ValueSource(strings = {"one""two""three"})
          @DisplayName("參數(shù)化測試1")
          public void parameterizedTest1(String string) {
              assertTrue(StringUtils.isNotBlank(string));
          }
          image-20210416233807174

          如果不是基礎(chǔ)的類型,可以使用方法構(gòu)造,只要返回值為Stream類型就可以,多個參數(shù)使用Arguments實(shí)例流

          @ParameterizedTest
          @MethodSource("method")
          @DisplayName("方法來源參數(shù)")
          public void testWithExplicitLocalMethodSource(String name) {
              Assertions.assertNotNull(name);
          }

          private static Stream<String> method() {
              return Stream.of("apple""banana");
          }

          @CsvSource允許您將參數(shù)列表表示為以逗號分隔的值(例如,字符串文字)

          @ParameterizedTest
          @CsvSource({"steven,18""jack,24"})
          @DisplayName("參數(shù)化測試-csv格式")
          public void parameterizedTest3(String name, Integer age) {
              System.out.println("name:" + name + ",age:" + age);
              Assertions.assertNotNull(name);
              Assertions.assertTrue(age > 0);
          }
          image-20210416232702304

          @CsvFileSource使用classpath中的CSV文件,CSV文件中的每一行都會導(dǎo)致參數(shù)化測試的一次調(diào)用

          這種就完全把測試數(shù)據(jù)與測試方法隔離,達(dá)到更好解耦效果

          @ParameterizedTest
          @CsvFileSource(resources = "/persons.csv")  //指定csv文件位置
          @DisplayName("參數(shù)化測試-csv文件")
          public void parameterizedTest2(String name, Integer age) {
              System.out.println("name:" + name + ",age:" + age);
              Assertions.assertNotNull(name);
              Assertions.assertTrue(age > 0);
          }

          其他方式不在贅述,如果還是滿足不了需求,可以通過@ArgumentsSource自定義自己的數(shù)據(jù)來源,必須封裝成去取JSON或者XMl等數(shù)據(jù)

          AssertJ

          當(dāng)定義好需要運(yùn)行的測試方法后,下一步則是需要關(guān)注測試方法的細(xì)節(jié),這就離不開斷言和假設(shè)

          斷言:封裝好了常用判斷邏輯,當(dāng)不滿足條件時(shí),該測試用例會被認(rèn)為測試失敗

          假設(shè):與斷言類似,當(dāng)條件不滿足時(shí),測試會直接退出而不是判定為失敗

          因?yàn)椴粫绊懙胶罄m(xù)的測試用例,最常用的還是斷言

          除了Junit5自帶的斷言,AssertJ是非常好用的一個斷言工具,最大特點(diǎn)是提供了流式斷言,與Java8使用方法非常類似

          @Test
          void testString() {
              // 斷言null或?yàn)榭兆址?/span>
              assertThat("").isNullOrEmpty();
              // 斷言空字符串
              assertThat("").isEmpty();
              // 斷言字符串相等 斷言忽略大小寫判斷字符串相等
              assertThat("niu").isEqualTo("niu").isEqualToIgnoringCase("NIu");
              // 斷言開始字符串 結(jié)束字符穿 字符串長度
              assertThat("niu").startsWith("ni").endsWith("u").hasSize(3);
              // 斷言包含字符串 不包含字符串
              assertThat("niu").contains("iu").doesNotContain("love");
              // 斷言字符串只出現(xiàn)過一次
              assertThat("niu").containsOnlyOnce("iu");
          }

          @Test
          void testNumber() {
              // 斷言相等
              assertThat(42).isEqualTo(42);
              // 斷言大于 大于等于
              assertThat(42).isGreaterThan(38).isGreaterThanOrEqualTo(38);
              // 斷言小于 小于等于
              assertThat(42).isLessThan(58).isLessThanOrEqualTo(58);
              // 斷言0
              assertThat(0).isZero();
              // 斷言正數(shù) 非負(fù)數(shù)
              assertThat(1).isPositive().isNotNegative();
              // 斷言負(fù)數(shù) 非正數(shù)
              assertThat(-1).isNegative().isNotPositive();
          }

          @Test
          void testCollection() {
              // 斷言 列表是空的
              assertThat(newArrayList()).isEmpty();
              // 斷言 列表的開始 結(jié)束元素
              assertThat(newArrayList(123)).startsWith(1).endsWith(3);
              // 斷言 列表包含元素 并且是排序的
              assertThat(newArrayList(123)).contains(1, atIndex(0)).contains(2, atIndex(1)).contains(3)
                  .isSorted();
              // 斷言 被包含與給定列表
              assertThat(newArrayList(312)).isSubsetOf(newArrayList(1234));
              // 斷言 存在唯一元素
              assertThat(newArrayList("a""b""c")).containsOnlyOnce("a");
          }

          @Test
          void testMap() {
              Map<String, Object> foo = ImmutableMap.of("A"1"B"2"C"3);

              // 斷言 map 不為空 size
              assertThat(foo).isNotEmpty().hasSize(3);
              // 斷言 map 包含元素
              assertThat(foo).contains(entry("A"1), entry("B"2));
              // 斷言 map 包含key
              assertThat(foo).containsKeys("A""B""C");
              // 斷言 map 包含value
              assertThat(foo).containsValue(3);
          }
          // 其他斷言,請自行探索......

          想想如果沒有使用AssertJ時(shí)我們是如何寫斷言的,是不是需要多個assert,很繁瑣

          AssertJ的斷言代碼清爽很多,流式斷言充分利用了java8之后的匿名方法和stream類型的特點(diǎn),很好的對Junit斷言方法做了補(bǔ)充。

          參考

          https://junit.org/junit5/docs/current/user-guide/#overview

          https://assertj.github.io/doc/


          瀏覽 30
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日韩综合在线 | 人人操大香蕉 | 亚洲中文无字幕 | 黄色电影视频在线 | 日韩污网站 |