JUnit 是 Java 中最流行的单元测试框架,Mockito 是一个流行的 mocking 框架,用于模拟和验证类的依赖关系,特别适用于单元测试中隔离被测试类的外部依赖。
简单的单元测试
添加依赖
在 pom.xml
文件中添加以下依赖
xml
<dependencies>
<!-- JUnit 5 测试运行器 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<!-- JUnit 5 断言库 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
创建待测试类
假设我们有一个简单的计算器类 Calculator
java
public class Calculator {
public int add(int num1, int num2) {
return num1 + num2;
}
public int subtract(int num1, int num2) {
return num1 - num2;
}
public int multiply(int num1, int num2) {
return num1 * num2;
}
public double divide(int num1, int num2) {
if (num2 == 0) {
throw new IllegalArgumentException("除数不能为 0");
}
return (double) num1 / num2;
}
}
创建测试类
在 src/test/java
目录下创建测试类 CalculatorTest
java
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class CalculatorTest {
private Calculator calculator = new Calculator();
// 测试加法方法
@Test
public void testAdd() {
int result = calculator.add(3, 5);
assertEquals(8, result, "加法运算结果不正确");
}
// 测试减法方法
@Test
public void testSubtract() {
int result = calculator.subtract(10, 4);
assertEquals(6, result, "减法运算结果不正确");
}
// 测试乘法方法
@Test
public void testMultiply() {
int result = calculator.multiply(6, 7);
assertEquals(42, result, "乘法运算结果不正确");
}
// 测试除法方法
@Test
public void testDivide() {
double result = calculator.divide(15, 3);
assertEquals(5.0, result, "除法运算结果不正确");
// 测试除数为 0 的情况
assertThrows(IllegalArgumentException.class, () -> calculator.divide(10, 0));
}
}
运行测试
在 IDE中可以直接右键点击测试类或测试方法,选择运行测试。也可以通过命令行运行测试,在项目根目录下执行 mvn test
命令

Spring Boot 项目单元测试
添加依赖
Spring Boot 项目可以添加<font style="color:rgb(13, 18, 57);">spring-boot-starter-test</font>
,内部包含多个测试工具
依赖名称 | 说明 |
---|---|
JUnit Jupiter | 支持 JUnit 5,用于编写和运行测试。 |
Spring Test | 提供Spring的测试支持,如@SpringBootTest 。 |
Mockito | 用于创建和管理模拟对象,支持依赖注入的测试。 |
AssertJ | 提供流畅且丰富的断言方法。 |
Hamcrest | 另一种断言库,常用于匹配和验证。 |
JSONassert | 用于JSON内容的断言。 |
JsonPath | 用于解析和验证JSON数据。 |
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
简单服务测试
假设存在一个简单的服务类 UserService
java
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public String getUserNameById(Long id) {
if (id == 1L) {
return "John";
}
return null;
}
}
对应的单元测试类 UserServiceTest 如下,直接创建 UserService 实例并调用其方法,使用 assertEquals 方法验证结果
java
package com.example.demo.service;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class UserServiceTest {
private UserService userService = new UserService();
@Test
void testGetUserNameById() {
String result = userService.getUserNameById(1L);
assertEquals("John", result);
}
}
带有依赖注入的服务测试
若 UserService 依赖其它组件,例如 UserRepository
java
package com.example.demo.service;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public String getUserNameById(Long id) {
return userRepository.findUserNameById(id);
}
}
这时候需要使用 Mockito 模拟 UserRepository 进行单元测试
@ExtendWith
启用Mockito扩展,支持注解初始化@Mock
注解模拟 UserRepository@InjectMocks
注解将模拟对象注入到 UserService 中when
方法定义模拟对象的行为
java
package com.example.demo.service;
import com.example.demo.repository.UserRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void testGetUserNameById() {
when(userRepository.findUserNameById(1L)).thenReturn("John");
String result = userService.getUserNameById(1L);
assertEquals("John", result);
}
}
控制器测试
假设存在一个控制器 UserController
java
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private final UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/users/{id}")
public String getUserName(@PathVariable Long id) {
return userService.getUserNameById(id);
}
}
这时候可以使用 Spring MVC Test 框架进行控制器单元测试
@WebMvcTest
注解用于测试控制器MockMvc
模拟 HTTP 请求@MockBean
模拟依赖的服务
java
package com.example.demo.controller;
import com.example.demo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void testGetUserName() throws Exception {
when(userService.getUserNameById(1L)).thenReturn("John");
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(content().string("John"));
}
}
测试报告
Surefire
Maven 提供了 Surefire Report Plugin,默认情况下当运行mvn test
时,Maven 会自动在 target/surefire-reports
目录下生成测试报告文件

这些报告文件通常是 XML 格式的,可以使用文本编辑器打开查看详细的测试结果,包括测试用例的执行情况、通过或失败的数量等信息,不过,XML 格式的报告阅读起来不太直观
JaCoCo
JaCoCo 是一个开源的 Java 代码覆盖率工具,在 Spring Boot 项目中使用 JaCoCo 可以生成详细的测试覆盖率报告。
xml
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.12</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
测试完成后,JaCoCo 会在 target/site/jacoco
目录下生成 HTML 格式的测试覆盖率报告。打开 index.html
文件,即可在浏览器中查看详细的报告内容,包括整体的测试覆盖率、各个类和方法的覆盖率情况等
当然也可以在 CI/CD流程中集成 JaCoCo,帮助开发者在自动化流程中监控代码的测试覆盖率