lsekfe 发表于 2020-12-7 11:20:54

基于JUnit5框架的Java单元测试流程

 1.原理图

2. 基本架构
  被测代码
package com.jerry;

  public class Calculator{

      private static int result;

      public void add(int n) {

        result = result + n;

      }

      public void substract(int n) {

        result = result - n;

      }

      public void multiply(int n) {

        result = result * n;

      }

      public void divide(int n){

        try {

              result = result / n;

        }catch(ArithmeticException ex){

              System.out.println(ex);

              throw new ArithmeticException("The n not allowed to 0!!")

        }

  }

  }最基本的测试代码
public class CalculatorTest {

      private static Calculator calculator = new Calculator();

      @BeforeEach

      public void setUp() throws Exception {

        calculator.clear();

      }

      @Test

      @DisplayName("测试加法")

      public void testAdd() {

        calculator.add(2);

        calculator.add(3);

        assertEquals(5, calculator.getResult());

      }

      @Test

      @DisplayName("测试减法")

      public void testSubstract() {

        calculator.add(5);

  calculator.substract(3);

        Assertions.assertEquals(2, calculator.getResult());

      }

      @Test

      @DisplayName("测试乘法")

      public void testMultiply() {

        calculator.add(3);

        calculator.multiply(2);

        Assertions.assertEquals(6, calculator.getResult());

      }

      @Test

      @DisplayName("测试除法")

      public void testDivide() {

        calculator.add(9);

        calculator.divide(3);

        Assertions.assertEquals(3, calculator.getResult());

      }标签@DisplayName可以在测试结果的时候显示其内容。

3.JUnit5的修饰符

描述装饰符的程序
 package com.jerry;

  import static org.junit.jupiter.api.Assertions.assertAll;

  import org.junit.jupiter.api.*;

  public class MyFirstJunit5Test {

  @BeforeAll

      @DisplayName("每条用例开始时执行")

  public static void startALL(){

  System.out.println("BeforeAll");

      }

  

  @AfterAll

      @DisplayName("每条用例开始时执行")

  public static void endAll(){

  System.out.println("AfterAll");

      }

  

  @BeforeEach

      @DisplayName("每条用例开始时执行")

      void start(){

  System.out.println("BeforeEach");

      }

      @AfterEach

      @DisplayName("每条用例结束时执行")

      void end(){

      System.out.println("AfterEach");

      }

      @Test

      void myFirstTest() {

      System.out.println("myFirstTest");

      Assertions.assertEquals(2, 1 + 1);

      }

      @Test

      @DisplayName("描述测试用例")

      void testWithDisplayName() {

      System.out.println("testWithDisplayName");

      }

      @Test

      @Disabled("这条用例暂时跑不过,忽略!")

      void myFailTest(){

      System.out.println("Disabled Testcase");

      Assertions.assertEquals(1,2);

      }

      @Test

      @DisplayName("运行一组断言")

      public void assertAllCase() {

      System.out.println("运行一组断言");

        assertAll("groupAssert",

                  () -> Assertions.assertEquals(2, 1 + 1),

                  () -> Assertions.assertTrue(1 > 0)

        );

      }

      @Test

      @DisplayName("依赖注入1")

      public void testInfo(final TestInfo testInfo) {

        System.out.println(testInfo.getDisplayName());

      }

      @Test

      @DisplayName("依赖注入2")

      public void testReporter(final TestReporter testReporter) {

        testReporter.publishEntry("name", "Alex");

      }

  }运行结果(缩进为了看起来方便,我自己设置的)
 BeforeAll

     BeforeEach

        依赖注入1

      AfterEach

      BeforeEach

        TestIdentifier [依赖注入2]

           ReportEntry

     AfterEach

      BeforeEach

        myFirstTest

      AfterEach

      BeforeEach

           testWithDisplayName

      AfterEach

      BeforeEach

         运行一组断言

      AfterEach

  AfterAll 注意:@BeforeAll、 @AfterALL注解方法必须是静态方法,否则会抛出运行时错误。  4. JUnit5 新加断言

5. 异常断言  被测的程序
public void divide(int n){

      try {

      result = result / n;

      }catch(ArithmeticException ex){

      System.out.println(ex);

      throw new ArithmeticException("The n not allowed to 0!!");

      }

      }测试程序
 @Test

      @DisplayName("测试除0异常")

      public void testDivideByZero() {

        calculator.add(9);

        Throwable exception = Assertions.assertThrows(ArithmeticException.class, () -> calculator.divide(0));

        Assertions.assertEquals("The n not allowed to 0!!",exception.getMessage());

  }6. 超时断言
@Test
      public void squareRoot(){

         Assertions.assertTimeoutPreemptively(Duration.of(2, ChronoUnit.SECONDS), () -> calculator.squareRoot(4))

  }

判断程序是否应该在两秒钟执行完毕。类似于JUnit4中的(timeout=XXX)。  7. 参数化测试
  7.1单参数
 @ParameterizedTest

  @ValueSource(ints = {1,2,3,4})

  @DisplayName("参数化测试_单参数")

  public void parameterizedTest(int param) {

      calculator.add(param);

      calculator.add(-1 * param);

      Assertions.assertEquals(0, calculator.getResult());

  }标签:  ·@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
  ·@NullSource: 表示为参数化测试提供一个null的入参
  ·@EnumSource: 表示为参数化测试提供一个枚举入参
  ValueSource类型:
  ·String values: @ValueSource(strings = {"foo", "bar", "baz"})
  ·Double values: @ValueSource(doubles = {1.5D, 2.2D, 3.0D})
  ·Long values: @ValueSource(longs = {2L, 4L, 8L})
  ·Integer values: @ValueSource(ints = {2, 4, 8})
  7.2 Enum参数
import java.util.concurrent.TimeUnit;

  …

  @ParameterizedTest

  @EnumSource(value = TimeUnit.class, names = {"SECONDS", "MINUTES"})

  @DisplayName("参数化测试_Enum参数")

  void testTimeUnitJustSecondsAndMinutes(TimeUnit unit) {

  Assertions.assertTrue(EnumSet.of(TimeUnit.SECONDS, TimeUnit.MINUTES).contains(unit));

  Assertions.assertFalse(EnumSet

            .of(TimeUnit.DAYS, TimeUnit.HOURS, TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS,

                  TimeUnit.MICROSECONDS).contains(unit));

  }7.3参数化测试——方法参数(多参数)  注意:这个方法返回必须是个流
   @ParameterizedTest

      @MethodSource("paramGenerator")

      @DisplayName("参数化测试_ 方法参数(多参数)")

      void MethodParameForSquareRoot(int param, int result){

        calculator.squareRoot(param);

        Assertions.assertEquals(result, calculator.getResult());

      }

  

      static Stream<Arguments> paramGenerator(){

        return Stream.of(Arguments.of(4,2), Arguments.of(9,3), Arguments.of(16,4));

      }7.4.参数化测试——CVS文件参数化

@ParameterizedTest

      @CsvFileSource(resources = "data.csv")//指定csv文件位置

      @DisplayName("参数化测试-csv文件")

      public void parameterizedCVSFile(int param, int result) {

        calculator.squareRoot(param);

        Assertions.assertEquals(result, calculator.getResult());

      }其中,data.csv为:
42

93

1648.内嵌测试类  一般一个产品类对应一个测试类,但是使用JUnit,可以实现类的嵌套。
package com.jerry;

  import org.junit.jupiter.api.BeforeEach;

  import org.junit.jupiter.api.DisplayName;

  import org.junit.jupiter.api.Nested;

  import org.junit.jupiter.api.Test;

  public class NestedTestDemo {

      @Test

      @DisplayName("Nested")

      void isInstantiatedWithNew() {

        System.out.println("最一层--内嵌单元测试");

      }

      @Nested

      @DisplayName("Nested2")

      class Nested2 {

        @BeforeEach

        void Nested2_init() {

              System.out.println("Nested2_init");

        }

        @Test

        void Nested2_test() {

              System.out.println("第二层-内嵌单元测试");

        }

        @Nested

        @DisplayName("Nested3")

        class Nested3 {

              @BeforeEach

              void Nested3_init() {

                  System.out.println("Nested3_init");

              }

              @Test

              void Nested3_test() {

                  System.out.println("第三层-内嵌单元测试");

              }

        }

      }

  }结果输出:  第一层--内嵌单元测试
  Nested2_init
  第二层-内嵌单元测试
  Nested2_init
  Nested3_init
  第三层-内嵌单元测试

9. 重复测试
 @RepeatedTest(5) //表示重复执行5次

  @DisplayName("重复减法")

  public void testSubstractManyTimes() {

      calculator.add(5);

      calculator.substract(3);

      Assertions.assertEquals(2, calculator.getResult());

  }这个测试用例将被执行5次,为什么设计这个方法,我个人没有理解。  10. 动态测试
<strong> </strong>@TestFactory

  @DisplayName("动态测试")

  Iterator<DynamicTest> dynamicTests() {

      return Arrays.asList(

        dynamicTest("第1个动态测试", () ->{calculator.squareRoot(4);Assertions.assertEquals(2, calculator.getResult());}),

        dynamicTest("第2个动态测试", () ->{calculator.squareRoot(9);Assertions.assertEquals(3, calculator.getResult());}),

        dynamicTest("第3个动态测试", () ->{calculator.squareRoot(16);Assertions.assertEquals(4, calculator.getResult());}),

        dynamicTest("第4个动态测试", () ->{calculator.squareRoot(25);Assertions.assertEquals(5, calculator.getResult());})

      ).iterator();

   }动态测试用得比较少,这个功能的原因我个人也不太理解。  11. 分组断言assertAll
 @Test

  @DisplayName("开根号分组断言")

  public void testGroup() {

      int[] parem = {4, 9, 16, 25, 36};

      int[] result ={2, 3, 4, 5, 6};

      Assertions.assertAll("parem,result",

        () -> {calculator.squareRoot(parem);Assertions.assertEquals(result, calculator.getResult());},

        () -> {calculator.squareRoot(parem);Assertions.assertEquals(result, calculator.getResult());},

        () -> {calculator.squareRoot(parem);Assertions.assertEquals(result, calculator.getResult());},

        () -> {calculator.squareRoot(parem);Assertions.assertEquals(result, calculator.getResult());},

        () -> {calculator.squareRoot(parem);Assertions.assertEquals(result, calculator.getResult());}

     );分组断言中任一个断言的失败,都会将以 MultipleFailuresError 错误进行抛出提示。另外可以看出,使用分组断言也可以实现参数化



















页: [1]
查看完整版本: 基于JUnit5框架的Java单元测试流程