Java案例如何实现单元测试?

wen python案例 2

本文目录导读:

Java案例如何实现单元测试?

  1. 环境搭建
  2. 测试目录结构
  3. 典型案例演示
  4. 编写单元测试
  5. 运行测试
  6. 常见注解速查表
  7. 关键原则

在Java中实现单元测试,最主流的方式是使用 JUnit 框架(目前主要使用 JUnit 5,即 Jupiter),配合 Mockito 等 Mock 框架来处理外部依赖。

以下是一个从环境搭建到编写、运行测试的完整指南。


环境搭建

Maven 依赖 (pom.xml)

<dependencies>
    <!-- JUnit 5 核心依赖 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.10.0</version>
        <scope>test</scope>
    </dependency>
    <!-- Mockito 用于 Mock 对象 -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>5.6.0</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Gradle 依赖 (build.gradle)

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    testImplementation 'org.mockito:mockito-core:5.6.0'
}
test {
    useJUnitPlatform()
}

测试目录结构

Maven/Gradle 标准项目中,测试代码放在:

src/
├── main/java/    # 生产代码
└── test/java/    # 测试代码(包名通常与生产代码一致)

典型案例演示

假设有一个待测试的 Calculator 类和一个需要 Mock 外部依赖的 UserService 类。

待测试类 A:纯业务逻辑

// src/main/java/com/example/Calculator.java
public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public int divide(int dividend, int divisor) {
        if (divisor == 0) {
            throw new IllegalArgumentException("Divisor cannot be zero");
        }
        return dividend / divisor;
    }
}

待测试类 B:依赖外部服务

// src/main/java/com/example/UserService.java
public class UserService {
    private final UserRepository userRepository;
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
    public String getUserNameById(int id) {
        User user = userRepository.findById(id);
        if (user == null) {
            throw new RuntimeException("User not found");
        }
        return user.getName();
    }
}
interface UserRepository {
    User findById(int id);
}
class User {
    private int id;
    private String name;
    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }
    public String getName() { return name; }
}

编写单元测试

测试 A:测试纯逻辑(无依赖)

// src/test/java/com/example/CalculatorTest.java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
    @Test
    void testAdd() {
        // 1. 准备(Arrange)
        Calculator calc = new Calculator();
        // 2. 执行(Act)
        int result = calc.add(3, 5);
        // 3. 断言(Assert)
        assertEquals(8, result);
    }
    @Test
    void testDivide() {
        Calculator calc = new Calculator();
        assertEquals(2, calc.divide(10, 5));
    }
    @Test
    void testDivideByZero() {
        Calculator calc = new Calculator();
        // 验证抛出指定异常
        assertThrows(IllegalArgumentException.class,
                     () -> calc.divide(10, 0));
    }
}

测试 B:测试有外部依赖的类(使用 Mockito)

// src/test/java/com/example/UserServiceTest.java
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
// 启用 Mockito 扩展(JUnit 5 方式)
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @Mock
    private UserRepository userRepository;  // 创建 Mock 对象
    @Test
    void testGetUserNameById_UserExists() {
        // 1. 准备(Arrange)
        // 定义 Mock 行为:当 findById(1) 被调用时,返回一个 User 对象
        when(userRepository.findById(1)).thenReturn(new User(1, "Alice"));
        UserService userService = new UserService(userRepository);
        // 2. 执行(Act)
        String name = userService.getUserNameById(1);
        // 3. 断言(Assert)
        assertEquals("Alice", name);
        // 4. 验证(Verify):确保 findById 确实被调用了一次
        verify(userRepository, times(1)).findById(1);
    }
    @Test
    void testGetUserNameById_UserNotFound() {
        // 准备:让 findById 返回 null
        when(userRepository.findById(anyInt())).thenReturn(null);
        UserService userService = new UserService(userRepository);
        // 执行 + 断言:期望抛出 RuntimeException
        assertThrows(RuntimeException.class,
                     () -> userService.getUserNameById(999));
    }
}

运行测试

IDE 内运行

  • IntelliJ IDEA:在测试类或方法左侧点击绿色三角形 ▶ 直接运行。
  • Eclipse:右键测试类 → Run AsJUnit Test

Maven 命令行

mvn test

Gradle 命令行

gradle test

常见注解速查表

注解 说明
@Test 标记一个测试方法
@BeforeEach 在每个测试方法前执行(初始化)
@AfterEach 在每个测试方法后执行(清理)
@BeforeAll 在所有测试方法前执行一次(静态方法)
@AfterAll 在所有测试方法后执行一次(静态方法)
@DisplayName("描述") 为测试类或方法添加中文/描述性名称
@ExtendWith(...) 注册扩展,如 MockitoExtension

重要断言方法:

  • assertEquals(expected, actual):判断相等
  • assertTrue(condition):判断为真
  • assertThrows(ExceptionClass, executable):判断抛异常
  • assertNotNull(object):判断不为 null

关键原则

  1. 独立性:每个测试方法不要依赖其他测试的结果。
  2. 可重复:每次运行结果一致(避免依赖随机数据、当前时间、网络等)。
  3. 单一职责:一个测试方法只测试一个行为或条件。
  4. 命名清晰:方法名最好能说明“在什么条件下,期望什么结果”,
    • testDivide_shouldThrowException_whenDivisorIsZero
  5. 使用 Mock:对外部服务、数据库、文件系统一律 Mock,测试只关注当前类的逻辑。

  • 纯逻辑:直接 new 目标类,调用方法,用 assertEquals 等断言。
  • 依赖外部:用 @Mock 创建 Mock 对象,用 when().thenReturn() 定义行为。
  • 运行方式:IDE 或 Maven/Gradle 命令行。
  • 行业标配:JUnit 5 + Mockito。

按照上述步骤和模板,你就可以在 Java 项目中系统化地编写单元测试了。

抱歉,评论功能暂时关闭!