JUnit5 实操

1.JUnit 的导入

<dependencies>
    <!-- JUnit Jupiter API + Engine -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-params</artifactId>
      <version>5.10.0</version>
      <scope>test</scope>
    </dependency>
</dependencies>

2.常用注解

注解

作用

@Test

声明一个测试方法

@BeforeEach

每个测试方法执行前执行

@AfterEach

每个测试方法执行后执行

@BeforeAll

所有测试开始前执行(需 static

@AfterAll

所有测试结束后执行(需 static

@DisplayName("xxx")

给测试类或方法自定义名称

@Disabled

禁用该测试方法

@Tag("xxx")

添加标签(分组执行测试用)

3.常用断言

Assertions.assertEquals(expected, actual); // 相等

Assertions.assertNotEquals(a, b); // 不相等

Assertions.assertTrue(expression); // 断言为 true

Assertions.assertFalse(expression); // 断言为 false

Assertions.assertNull(obj); // 是 null

Assertions.assertNotNull(obj); // 不是 null

Assertions.assertThrows(ArithmeticException.class, () -> 1 / 0); // 异常断言

4.参数测试

基本注解:@ParameterizedTest

替代 @Test,并结合不同的数据源注解,比如:

@ValueSource:传入基础类型

@CsvSource:传入多个参数(逗号分隔)

@MethodSource:使用方法提供参数

@EnumSource:枚举测试

@ArgumentsSource:自定义参数源

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;

import java.util.stream.Stream;

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

// 枚举定义
enum Status {
    NEW, IN_PROGRESS, DONE
}

public class ParameterizedTestExample {

    // 1. @ValueSource 示例:基础类型参数(int / String / long / double)
    @ParameterizedTest
    @ValueSource(strings = {"hello", "junit", "test"})
    @DisplayName("测试字符串长度为非空")
    void testWithValueSource(String word) {
        assertNotNull(word);
        assertTrue(word.length() > 0);
    }

    // 2. @CsvSource 示例:多参数输入
    @ParameterizedTest
    @CsvSource({
        "1, 2, 3",
        "2, 3, 5",
        "5, 7, 12"
    })
    @DisplayName("测试加法")
    void testWithCsvSource(int a, int b, int expectedSum) {
        assertEquals(expectedSum, a + b);
    }

    // 3. @MethodSource 示例:方法提供复杂参数(支持对象)
    @ParameterizedTest
    @MethodSource("provideStringsForIsBlank")
    @DisplayName("测试字符串是否为空白")
    void testWithMethodSource(String input, boolean expected) {
        assertEquals(expected, input == null || input.trim().isEmpty());
    }

    static Stream<Arguments> provideStringsForIsBlank() {
        return Stream.of(
            Arguments.of("   ", true),
            Arguments.of("", true),
            Arguments.of(null, true),
            Arguments.of("abc", false)
        );
    }

    // 4. @EnumSource 示例:遍历枚举值
    @ParameterizedTest
    @EnumSource(Status.class)
    @DisplayName("测试所有状态枚举非空")
    void testWithEnumSource(Status status) {
        assertNotNull(status);
    }

    // 5. @ArgumentsSource 示例:自定义参数提供器
    @ParameterizedTest
    @ArgumentsSource(CustomStringProvider.class)
    @DisplayName("使用自定义参数源")
    void testWithArgumentsSource(String input) {
        assertTrue(input.startsWith("arg"));
    }

    // 内部类:实现 ArgumentsProvider 接口
    static class CustomStringProvider implements ArgumentsProvider {
        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of("arg1", "arg2", "arg3").map(Arguments::of);
        }
    }
}

5.实践例子

import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.*;

import java.util.Map;
import java.util.stream.Stream;

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

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisplayName("🧪 UserService CRUD 测试")
@Tag("user")
public class UserServiceTest {

    private UserService userService;

    @BeforeAll
    static void beforeAll() {
        System.out.println("🚀 启动 UserService 测试...");
    }

    @AfterAll
    static void afterAll() {
        System.out.println("✅ 所有测试已完成");
    }

    @BeforeEach
    void setUp() {
        userService = new UserService();
    }

    @AfterEach
    void tearDown() {
        System.out.println("--- 单个测试完成 ---");
    }

    @Test
    @DisplayName("创建用户应返回ID并可查")
    void testCreateAndGetUser() {
        Long id = userService.createUser("Alice");
        assertNotNull(id);
        assertEquals("Alice", userService.getUser(id));
    }

    @Test
    @DisplayName("删除用户成功后不能再获取")
    void testDeleteUser() {
        Long id = userService.createUser("Bob");
        assertTrue(userService.deleteUser(id));
        assertNull(userService.getUser(id));
    }

    @Test
    @DisplayName("更新用户名称成功")
    void testUpdateUser() {
        Long id = userService.createUser("Charlie");
        boolean updated = userService.updateUser(id, "Charles");
        assertTrue(updated);
        assertEquals("Charles", userService.getUser(id));
    }

    @Test
    @DisplayName("获取所有用户")
    void testListUsers() {
        userService.createUser("Tom");
        userService.createUser("Jerry");
        Map<Long, String> users = userService.listUsers();
        assertEquals(2, users.size());
        assertTrue(users.containsValue("Tom"));
        assertTrue(users.containsValue("Jerry"));
    }

    @Test
    @DisplayName("创建空用户名应抛异常")
    void testCreateUserWithNullName() {
        assertThrows(IllegalArgumentException.class, () -> userService.createUser(" "));
    }

    // 🔁 参数化测试:@ValueSource
    @ParameterizedTest
    @ValueSource(strings = { "Alice", "Bob", "Charlie" })
    @DisplayName("使用不同名称创建用户")
    void testCreateWithValueSource(String name) {
        Long id = userService.createUser(name);
        assertNotNull(userService.getUser(id));
    }

    // 🔁 参数化测试:@CsvSource
    @ParameterizedTest
    @CsvSource({
        "John, Johnny",
        "Lucy, Lucille"
    })
    @DisplayName("创建并更新用户名")
    void testUpdateWithCsvSource(String original, String updated) {
        Long id = userService.createUser(original);
        assertTrue(userService.updateUser(id, updated));
        assertEquals(updated, userService.getUser(id));
    }

    // 🔁 参数化测试:@MethodSource
    @ParameterizedTest
    @MethodSource("provideUserNames")
    @DisplayName("使用方法源批量创建用户")
    void testCreateWithMethodSource(String name) {
        Long id = userService.createUser(name);
        assertNotNull(id);
        assertEquals(name, userService.getUser(id));
    }

    static Stream<Arguments> provideUserNames() {
        return Stream.of(
            Arguments.of("Lily"),
            Arguments.of("James"),
            Arguments.of("Henry")
        );
    }

    // 🔁 参数化测试:@EnumSource
    @ParameterizedTest
    @EnumSource(UserService.Role.class)
    @DisplayName("枚举角色不为空")
    void testWithEnumSource(UserService.Role role) {
        assertNotNull(role);
    }

    // 🔁 参数化测试:@ArgumentsSource(自定义)
    @ParameterizedTest
    @ArgumentsSource(CustomNameProvider.class)
    @DisplayName("使用自定义名字创建用户")
    void testWithArgumentsSource(String name) {
        Long id = userService.createUser(name);
        assertTrue(userService.getUser(id).startsWith(name.substring(0, 1)));
    }

    static class CustomNameProvider implements ArgumentsProvider {
        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of("Alan", "Amy", "Ann").map(Arguments::of);
        }
    }

    // 🚫 被禁用的测试
    @Test
    @Disabled("逻辑尚未实现")
    void testFindUserByEmail() {
        fail("该功能尚未开发");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值