单元测试之JUnit5使用

概述

我们常说的JUnit5包含三部分:

JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage

  • JUnit Platform: 是JVM上启动测试框架的基础。提各种平台或者IDE提醒接入测试框架的能力,日常编写测试代码时不用太关心此模块
  • JUnit Vintage: 一个兼容Junit3/4的测试引擎,如果不混用Junit5和低版本,不用引入此模块
  • Junit Jupiter: Junit5的核心依赖模块,是编程模型(提供我们编写测试代码时各种依赖)和扩展模型(提供使用或编写各种扩展的依赖)的组合。

依赖

Junit5需要运行在Java8或更高的版本

编写测试准备

以Maven项目为例

添加Junit5依赖

<dependencies>
    <!-- JUnit 5 核心依赖 除了参数化测试外的 编程模型和扩展模型的核心包-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>${junit-jupiter.version}</version>
        <scope>test</scope>
    </dependency>
    <!-- JUnit 5 参数化测试的依赖,不使用@ParameterizedTest的话可以不引入,但是怎么可能不使用呢?-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-params</artifactId>
        <version>${junit-jupiter.version}</version>
        <scope>test</scope>
    </dependency>

    <!-- JUnit 5 测试引擎-->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>${junit-jupiter.version}</version>
        <scope>test</scope>
    </dependency>
</dependencies>

配置Maven Surefire 插件

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>${maven-surefire-plugin.version}</version>
            <configuration>
                <includes>
                    <include>**/*Test.java</include>
                </includes>
            </configuration>
        </plugin>
    </plugins>
</build>

 编写测试类

按照Maven惯例在 src/test/java 中创建测试类:

两个小概念

  • 测试方法:任何直接注解或元注解了 @Test、@RepeatedTest、@ParameterizedTest、@TestFactory 或 @TestTemplate 的实例方法。
  • 测试类:任何顶层类、static 成员类或@Nested 类,其中包含至少一个测试方法,即容器。测试类不能是 abstract 的,并且必须具有单个构造函数。
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {
    
    @Test
    void testAdd() {
        Calculator calculator = new Calculator();
        assertEquals(5, calculator.add(2, 3));
    }

    @Test
    void testDivideByZero() {
        Calculator calculator = new Calculator();
        assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));
    }
}

参数化测试

前天必须引入junit-jupiter-params依赖

我常用的时@ValueSource和@MethodSource

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import static org.junit.jupiter.api.Assertions.*;

public class CalculatorParameterizedTest {

    // 被测试的类
    private final Calculator calculator = new Calculator();

    // 1. 基础参数化测试:单参数输入(测试平方)
    @ParameterizedTest
    @ValueSource(ints = {2, -3, 0}) // 提供三个测试输入
    void testSquare(int number) {
        int result = calculator.square(number);
        assertEquals(number * number, result);
    }

    // 2. 多参数输入测试(测试加法)
    @ParameterizedTest
    @CsvSource({
        "5, 3, 8",    // 5+3=8
        "0, 0, 0",    // 0+0=0
        "-2, 5, 3"    // -2+5=3
    })
    void testAdd(int a, int b, int expected) {
        int result = calculator.add(a, b);
        assertEquals(expected, result);
    }

    // 3. 参数来源为方法(测试除法)
    @ParameterizedTest
    @MethodSource("provideDivisionTestData") // 从方法获取数据
    void testDivide(int dividend, int divisor, int expected) {
        int result = calculator.divide(dividend, divisor);
        assertEquals(expected, result);
    }

    // 提供测试数据的方法(必须是静态的)
    private static Stream<Arguments> provideDivisionTestData() {
        return Stream.of(
            Arguments.of(10, 2, 5),
            Arguments.of(0, 5, 0),
            Arguments.of(-9, 3, -3)
        );
    }

    // 4. 参数化异常测试(除零异常)
    @ParameterizedTest
    @CsvSource({"10, 0", "-5, 0"})
    void testDivideByZero(int dividend, int divisor) {
        assertThrows(ArithmeticException.class, () -> calculator.divide(dividend, divisor));
    }
}

// 计算器类(新增平方方法)
class Calculator {
    int add(int a, int b) { return a + b; }
    int square(int n) { return n * n; }
    int divide(int a, int b) { return a / b; }
}

 

生命周期

什么是生命周期方法?

任何直接注解或元注解了 @BeforeAll、@AfterAll、@BeforeEach 或 @AfterEach 的方法。

注释解释

注解作用执行顺序
@BeforeAll 整个测试类执行前运行一次(常用于初始化全局资源,如数据库连接) 1
@BeforeEach 每个测试方法执行前运行(常用于重置测试数据或准备环境) 2
@Test 标记一个测试方法 3
@AfterEach 每个测试方法执行后运行(常用于清理资源,如关闭文件) 4
@AfterAll 整个测试类执行后运行一次(常用于释放全局资源,如关闭数据库连接) 5

例子

import org.junit.jupiter.api.*;

public class CalculatorLifecycleTest {

    // 1. 整个测试类初始化(只执行一次)
    @BeforeAll
    static void initAll() {
        System.out.println("==== 测试类开始 ====");
    }

    // 2. 每个测试方法前的初始化(每个测试方法执行前都会调用)
    @BeforeEach
    void initEach() {
        System.out.println("\n--- 准备执行新测试 ---");
    }

    // 测试方法 1:加法测试
    @Test
    void testAdd() {
        Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        Assertions.assertEquals(5, result);
        System.out.println("加法测试通过 ✅");
    }

    // 测试方法 2:除法测试
    @Test
    void testDivide() {
        Calculator calculator = new Calculator();
        Assertions.assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));
        System.out.println("除法异常测试通过 ✅");
    }

    // 3. 每个测试方法后的清理(每个测试方法执行后都会调用)
    @AfterEach
    void cleanUpEach() {
        System.out.println("--- 当前测试完成 ---");
    }

    // 4. 整个测试类结束后的清理(只执行一次)
    @AfterAll
    static void cleanUpAll() {
        System.out.println("\n==== 测试类结束 ====");
    }
}

// 简单的计算器类
class Calculator {
    int add(int a, int b) {
        return a + b;
    }

    int divide(int a, int b) {
        return a / b;
    }
}

执行结果

==== 测试类开始 ====

--- 准备执行新测试 ---
加法测试通过 ✅
--- 当前测试完成 ---

--- 准备执行新测试 ---
除法异常测试通过 ✅
--- 当前测试完成 ---

==== 测试类结束 ====

 定义及使用扩展

参考

官网:https://junit.cn/junit5/docs/current/user-guide/#overview

以上例子都是DeepSeek写的

 

posted @ 2025-03-28 09:20  halu126  阅读(254)  评论(0)    收藏  举报