前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、junit5 介绍

1、JUnit5 构成

JUnit5 由三个不同子项目的几个不同模块组成。

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

1)JUnit Platform: 是在 JVM 上启动测试框架的基础,不仅支持 Junit 自制的测试引擎,其他测试引擎也都可以接入。

2)JUnit Jupiter: 提供了 JUnit5 的新的编程模型,是 JUnit5 新特性的核心。内部包含了一个测试引擎,用于在 Junit Platform 上运行。

3)JUnit Vintage: 由于 JUint 已经发展多年,为了照顾老的项目,其提供了兼容 JUnit4.x , Junit3.x 的测试引擎。

2、Pom

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

二、Junit5 基础方法

1、@BeforeEach@AfterEach@BeforeAll@AfterAll

注解 功能
@BeforeEach 表示在每个单元测试之前执行
@AfterEach 表示在每个单元测试之后执行
@BeforeAll 表示在所有单元测试之前执行
@AfterAll 表示在所有单元测试之后执行
@Slf4j
@DisplayName("junit5功能测试")
@SpringBootTest
public class Junit5BaseTest {


    @DisplayName("测试displayname注解")
    @Test
    void testDisplayName() {
        System.out.println(1);
    }

    /**
     * 所有测试方法运行前运行
     */
    @BeforeAll
    public static void beforeAll() {
        System.out.println("Run before all test methods run");
    }

    /**
     * 每个测试方法运行前运行
     */
    @BeforeEach
    public void beforeEach() {
        System.out.println("Run before each test method runs");
    }

    /**
     * 每个测试方法运行完毕后运行
     */
    @AfterEach
    public void afterEach() {
        System.out.println("Run after each test method finishes running");
    }

    /**
     * 在所有测试方法运行完毕后运行
     */
    @AfterAll
    public static void afterAll() {
        System.out.println("Run after all test methods have finished running");
    }

}

image-20230323155042117

2、断言

1)简单断言 Assertions.assert……

方法 说明
Assertions.assertEquals 判断两个对象或两个原始类型是否相等
Assertions.assertNotEquals 判断两个对象或两个原始类型是否不相等
Assertions.assertSame 判断两个对象引用是否指向同一个对象
Assertions.assertNotSame 判断两个对象引用是否指向不同的对象
Assertions.assertTrue 判断给定的布尔值是否为 true
Assertions.assertFalse 判断给定的布尔值是否为 false
Assertions.assertNull 判断给定的对象引用是否为 null
Assertions.assertNotNull 判断给定的对象引用是否不为 null
Assertions.assertTrue 判断条件是否满足

2)数组断言 assertArrayEquals

通过 assertArrayEquals方法来判断两个对象或原始类型的数组是否相等

@Test
@DisplayName("array assertion")
public void array() {
    assertArrayEquals(new int[]{1, 2}, new int[] {1, 2});
}

image-20230323164334451

3)组合断言 assertAll

assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言

1、可以通过 lambda 表达式很容易的提供这些断言

2、前边断言失败,后续代码不会执行

3、组合断言,当组合中的所有断言都通过才是true,否则是false

@Test
@DisplayName("assert all")
public void all() {
    assertAll("组合断言失败",
            () -> Assertions.assertEquals(2, 1 + 1),
            () -> Assertions.assertTrue(false)
    );
}

image-20230323164310722

4)异常断言 Assertions.assertThrows()

JUnit5 提供了 **Assertions.assertThrows() ** ,配合函数式编程就可以进行使用。

@Test
@DisplayName("异常断言")
public void exceptionTest() {
    Assertions.assertThrows(
            ArithmeticException.class, () -> {
                int i = 1 % 0;
            });

}

image-20230323164204120

5)超时断言 Assertions.assertTimeout()

` Junit5 提供了 **Assertions.assertTimeout()`** 为测试方法设置了超时时间

@Test
@DisplayName("超时测试(如果测试方法时间超过1s将会异常)")
public void timeoutTest() {
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
}

image-20230323164154058

6)快速失败

1、当满足某一条件,需要退出测试程序时,可以使用快速失败方法

2、当调用快速失败方法时,程序会退出,同时输出失败提示

@Test
@DisplayName("fail")
public void shouldFail() {
    fail("This should fail");
}

image-20230323164134747

3、前置条件

JUnit 5 中的前置条件(又叫假设)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要

注意:前置条件与断言最大的区别就是前置条件不满足的情况下,跳过该方法继续执行别的方法,而不满足断言的话后面的代码都将不会执行

⬤ 当不满足前置条件时,后续输出代码未执行

⬤ 当不满足前置条件时,程序未报错退出,而是终止执行

@DisplayName("测试前置条件")
@Test
void testAssumptions() {
    Assumptions.assumeTrue(false, "结果不足true");
    log.info("[Junit5Test#testAssumptions] 结果不足true");
}

image-20230323164123359

4、嵌套测试

JUnit 5 可以通过 Java 中的内部类和 @Nested 注解实现嵌套测试

1、外层测试方法的执行,不会驱动内层方法:当内层方法中有 beforeafter 等注解时,调用外层测试方法,并不会驱动其提前或最后执行

2、内层方法的执行,可以驱动外层方法:当外层方法定义了某一数据结果时,内层测试方法可以直接调用该结构

  @Test
  @DisplayName("超时测试(如果测试方法时间超过1s将会异常)")
  public void timeoutTest() {
      Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(500));
  }

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

      @BeforeEach
      void innerBeforeEach() {
          log.info("[InnerTest#innerBeforeEach] innerBeforeEach");
      }


      @Test
      @DisplayName("is empty")
      void isEmpty() {
          timeoutTest();
          assertTrue(true, "参数不为true");
      }

  }

image-20230323170525603

image-20230323170535526

5)参数化测试

注解 功能
@ValueSource 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource 表示为参数化测试提供一个null的入参
@EnumSource 表示为参数化测试提供一个枚举入参
@CsvFileSource 表示读取指定CSV文件内容作为参数化测试入参
@CsvFileSource 表示读取指定CSV文件内容作为参数化测试入参
@MethodSource 表示读取指定方法的返回值作为参数化测试入参(方法的返回值为流)

1)@ValueSource

入参为基础类型

@ParameterizedTest
  @DisplayName("参数化测试")
  @ValueSource(ints = {1, 2, 3, 4, 5})
  void testParamterized(int i) {
      System.out.println(i);java
  }

image-20230323170914400

2)@MethodSource 入参为方法返回值类型

@ParameterizedTest
@DisplayName("参数化方法测试")
@MethodSource("stringProvider")
void testParamterized2(String s) {
    System.out.println(s);
}

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

image-20230323171052007

4、Test 注解内部 (Junit4

1)timeout

timeout 属性用于指定测试方法允许运行的最大时间。如果测试方法在这个时间内没有完成,测试将被视为失败。

/**
 * junit4 超时
 */
@Test(timeout = 1000) // 1秒超时
public void junit4TestMethodWithTimeout() throws InterruptedException {
    // 测试代码,模拟长时间运行的任务
    Thread.sleep(500); // 0.5秒,测试会通过
    // Thread.sleep(1500); // 1.5秒,测试会失败
}


/**
 * junit5 超时
 */
@org.junit.jupiter.api.Test() // 1秒超时
void junit5testMethodWithTimeout() {
    Assertions.assertTimeout(Duration.ofSeconds(1), () -> {
        // 测试代码,模拟长时间运行的任务
        Thread.sleep(500); // 0.5秒,测试会通过
        // Thread.sleep(1500); // 1.5秒,测试会失败
    });
}

2)expected

expected 属性(尽管不是 @Test 注解的直接属性)用于指定测试方法期望抛出的异常类型。如果测试方法没有抛出指定的异常,测试将被视为失败。

/**
 * junit4 异常捕获
 */
@Test(expected = IllegalArgumentException.class)
public void junit4Exception(){
    // 测试代码,期望抛出IllegalArgumentException
    throw new IllegalArgumentException("This is an expected exception");
}


/**
 * junit5 异常捕获
 */
@org.junit.jupiter.api.Test()
public void junit5Exception(){
    Assertions.assertThrows(IllegalArgumentException.class, () -> {
        // 测试代码,期望抛出IllegalArgumentException
        throw new IllegalArgumentException("This is an expected exception");
    });
}

ContactAuthor