本文目录导读:

在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 As→JUnit 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
关键原则
- 独立性:每个测试方法不要依赖其他测试的结果。
- 可重复:每次运行结果一致(避免依赖随机数据、当前时间、网络等)。
- 单一职责:一个测试方法只测试一个行为或条件。
- 命名清晰:方法名最好能说明“在什么条件下,期望什么结果”,
testDivide_shouldThrowException_whenDivisorIsZero
- 使用 Mock:对外部服务、数据库、文件系统一律 Mock,测试只关注当前类的逻辑。
- 纯逻辑:直接
new目标类,调用方法,用assertEquals等断言。 - 依赖外部:用
@Mock创建 Mock 对象,用when().thenReturn()定义行为。 - 运行方式:IDE 或 Maven/Gradle 命令行。
- 行业标配:JUnit 5 + Mockito。
按照上述步骤和模板,你就可以在 Java 项目中系统化地编写单元测试了。