织梦网站模板本地安装教程,wordpress主题无法上传,可以做文档赚钱的网站,dw网页代码1. 什么是单元测试
1.1 基本定义
单元测试(Unit Test) 是对软件开发中最小可测单位#xff08;例如一个方法或者一个类#xff09;进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中#xff0c;单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快…1. 什么是单元测试
1.1 基本定义
单元测试(Unit Test) 是对软件开发中最小可测单位例如一个方法或者一个类进行验证的一种测试方式。在 Java 后端的 Spring Boot 项目中单元测试通常会借助 JUnit、Mockito 等框架对代码中核心逻辑进行快速且隔离的验证保证功能正确性。
目的及早发现并修复 BUG使后续迭代功能或重构时能迅速验证不会破坏已实现的功能。
1.2 单元测试在 Spring Boot 中的地位
Spring Boot 提供了非常方便的测试支持如 SpringBootTest、TestConfiguration 等注解让开发者可以快速地在带有 Spring 容器上下文的环境中执行测试。Spring Boot 本身也对 JUnit、Mockito、AssertJ 等常用测试框架或库提供了开箱即用的整合或依赖。
1.3 单元测试与其他测试的区别
单元测试聚焦在一个方法或者一个类层面不涉及过多外部依赖能极快地发现逻辑错误。集成测试多个模块或组件交互时的测试通常依赖真实数据库、消息队列等外部资源。端到端测试(E2E)关注的是整个系统的完整流程包括前端、后端、数据库、外部接口等。在 Spring Boot 环境中可以使用 SpringBootTest 搭配 Mock 或者内存数据库来实现集成测试但这通常已经不只是“单元”级别了。
2. 为什么要写单元测试
快速发现 Bug写完代码马上测不用等到上线才被发现问题。减少回归成本以后代码改动或升级只要一键跑测试就能知道改动有没有影响其他功能。保证代码质量养成单元测试的习惯会促使你把代码设计得更简洁和更容易测试。
简单说花小时间写单元测试能为你省下大时间修 Bug。 3. 环境准备
3.1 依赖
在一个常规的 Spring Boot 项目中只要在 pom.xmlMaven或 build.gradleGradle 里加上
!-- 如果是 Maven --
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope
/dependencyJUnit 5最常用的Java测试框架写 Test 方法Mockito常用的“模拟”库用来Mock其他依赖AssertJ / Hamcrest更好用的断言库Spring Test / Spring Boot TestSpring官方提供的测试辅助
这也就够了一般不需要额外安装别的。
3.2 项目结构
Spring Boot常见的目录结构Maven示例
src├─ main│ └─ java│ └─ com.example.demo│ ├─ DemoApplication.java│ └─ service│ └─ MyService.java└─ test└─ java└─ com.example.demo├─ DemoApplicationTests.java└─ service└─ MyServiceTest.javasrc/main/java 放你的业务代码。src/test/java 放你的测试代码。通常测试类的包路径要和被测类一致这样在IDE里能很快对上号也方便管理。 4. 最最简单的单元测试示例不依赖Spring
先从“纯JUnit”说起最简单的情况就是
我有一个普通的工具类/方法我就想测试它的输入输出对不对不用装载Spring也不用什么复杂注解
代码示例
假设我们有一个简单的工具类
public class MathUtil {public static int add(int a, int b) {return a b;}
}那我们写一个测试类路径src/test/java/.../MathUtilTest.java
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;public class MathUtilTest {Testvoid testAdd() {int result MathUtil.add(2, 3);Assertions.assertEquals(5, result, 2 3 应该等于 5);}
}Test 表示这是一个测试方法。Assertions.assertEquals(期望值, 实际值, 提示信息) 用来断言。 如果断言不通过测试就失败通过则测试成功。
运行方法
在 IDE如 IntelliJ/ Eclipse里右键这个 MathUtilTest 类 - Run MathUtilTest或者在命令行里运行 mvn testMaven / gradle testGradle。
这就是最最基础的单元测试。 5. 在 Spring Boot 里测试 - Service层
当你要测试一个 Service业务逻辑类 时它可能依赖其他Bean例如 Repository、Dao 等或者需要 Autowired。在 Spring Boot 里有两种主要方法
方法1纯Mock不启动Spring Context 适合只想测试这个Service逻辑本身不需要真的连数据库也不需要整个Spring环境。速度最快。 用 Mockito 来创建一个假的Mock依赖。注入到要测的Service里这样你可以控制依赖的行为。
示例
UserRepository.java (假设它是个接口用来访问数据库)
public interface UserRepository {User findByName(String name);// ... 其他方法
}UserService.java (我们要测这个类)
public class UserService {private UserRepository userRepository;// 通过构造注入依赖public UserService(UserRepository userRepository) {this.userRepository userRepository;}public String getUserNickname(String name) {User user userRepository.findByName(name);if (user null) {return UNKNOWN;}return user.getNickname();}
}UserServiceTest.java (测试类不依赖 Spring)
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Assertions;
import org.mockito.Mockito;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;ExtendWith(MockitoExtension.class) // JUnit5 启用Mockito
public class UserServiceTest {Mockprivate UserRepository userRepository; // Mock出来的依赖InjectMocksprivate UserService userService; // 要测试的对象会把上面这个Mock自动注入进来Testvoid testGetUserNickname_found() {// 1. 假设我们模拟一个“数据库中查到的用户”User mockUser new User();mockUser.setName(alice);mockUser.setNickname(AliceWonder);// 2. 定义假数据的返回行为Mockito.when(userRepository.findByName(alice)).thenReturn(mockUser);// 3. 调用被测方法String nickname userService.getUserNickname(alice);// 4. 断言结果Assertions.assertEquals(AliceWonder, nickname);}Testvoid testGetUserNickname_notFound() {// 没有设置when则默认返回nullString nickname userService.getUserNickname(bob);Assertions.assertEquals(UNKNOWN, nickname);}
}使用了 Mock 注解声明要模拟的依赖 userRepository。使用了 InjectMocks 注解告诉 Mockito要把所有标记 Mock 的对象注入进 UserService。这样就能让 UserService 这个对象在执行时使用模拟过的 userRepository 而不访问真实数据库。然后通过 Mockito.when(...) 来定义依赖方法的返回值用于测试用例的前提条件设置。通过 Assertions 来验证执行结果是否符合预期。
这样就只测 UserService 的逻辑不会真的访问数据库也不需要启动Spring执行很快。
方法2使用 SpringBootTest (集成上下文) 适合你想在测试时使用Spring管理Bean比如自动注入 Autowired或想测试和别的Bean的连接配置是否正常。 在测试类上加 SpringBootTest。这样Spring容器会启动你也能 Autowired 你的Service或者别的Bean。
示例
UserService.java (类似前面只不过换成了 Spring注入)
Service
public class UserService {Autowiredprivate UserRepository userRepository;public String getUserNickname(String name) {User user userRepository.findByName(name);if (user null) {return UNKNOWN;}return user.getNickname();}
}UserServiceSpringTest.java (测试类使用Spring上下文)
SpringBootTest
public class UserServiceSpringTest {Autowiredprivate UserService userService;MockBeanprivate UserRepository userRepository; // MockBean的意思Spring 启动时// 把真正的UserRepository替换成一个Mock对象// 我们就可以定义它的返回值而不会真的连数据库Testvoid testGetUserNickname_found() {User mockUser new User();mockUser.setName(alice);mockUser.setNickname(AliceWonder);Mockito.when(userRepository.findByName(alice)).thenReturn(mockUser);String result userService.getUserNickname(alice);Assertions.assertEquals(AliceWonder, result);}Testvoid testGetUserNickname_notFound() {// 不设置when就会返回nullString result userService.getUserNickname(unknown);Assertions.assertEquals(UNKNOWN, result);}
}SpringBootTest会启动一个小型Spring环境让 Autowired 能起作用。MockBean 可以让你把某个Bean比如 UserRepository变成一个模拟对象。整体执行依然比较快但比纯Mock稍微慢一点因为要先启动Spring容器。 6. 测试 Controller 层
在 Spring Boot 里Controller 是对外的 HTTP 接口。最常见的两种测试方式 用 WebMvcTest MockMvc不启动整个应用只启动Web层速度较快用 SpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT) TestRestTemplate会真正启动一个内嵌服务器发起真实HTTP请求更贴近实际环境。 6.1 WebMvcTest 示例
WebMvcTest(UserController.class) // 表示只测 UserController 相关
public class UserControllerTest {Autowiredprivate MockMvc mockMvc; // 用来模拟HTTP请求MockBeanprivate UserService userService; // Mock掉Service层Testvoid testGetUser() throws Exception {// 假设Service返回一个User对象User mockUser new User();mockUser.setName(test);mockUser.setNickname(TestNick);// 定义service行为Mockito.when(userService.getUserNickname(test)).thenReturn(TestNick);// 用MockMvc发起GET请求对应Controller的 /user/{name} 路径mockMvc.perform(MockMvcRequestBuilders.get(/user/test)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().string(TestNick));}
}WebMvcTest 只会扫描和加载 Web 层相关的组件不会启动整个 Spring Boot 应用测试速度更快。mockMvc.perform(get(/users/1)) 可以模拟一次 GET 请求到 /users/1并断言返回的 JSON 结构和内容。
6.2 SpringBootTest TestRestTemplate
如果你想做一个更真实的集成测试包括 Controller、Service、Repository 等所有层可以使用 SpringBootTest 并设置 webEnvironment RANDOM_PORT 或 DEFINED_PORT 来启动内嵌服务器然后注入 TestRestTemplate 来请求
SpringBootTest(webEnvironment SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UserControllerIntegrationTest {Autowiredprivate TestRestTemplate restTemplate; // 可以真的发请求Testvoid testGetUser() {// 假设数据库里已经有对应数据或者你用 MockBean 替换依赖String result restTemplate.getForObject(/user/test, String.class);Assertions.assertEquals(TestNick, result);}
}这里会真正启动一个随机端口的Tomcat然后 TestRestTemplate 真的去请求本地这个 /user/test 接口。非常贴近真实部署只是适合做集成测试比前面的MockMvc测试稍慢一点。 7. 常见的断言与技巧
7.1 断言
Assertions.assertEquals(期望, 实际)断言二者相等。Assertions.assertTrue(条件)断言条件为真。Assertions.assertThrows(异常类型, 代码块)断言执行代码块会抛出指定异常。
例如
Test
void testThrowException() {Assertions.assertThrows(IllegalArgumentException.class, () - {// 假设调用了一个会抛出异常的方法someMethod(null);});
}7.2 Mock时常用的 Mockito 方法
Mockito.when( mockObj.方法(...) ).thenReturn(返回值);Mockito.when( mockObj.方法(...) ).thenThrow(异常);Mockito.verify( mockObj, Mockito.times(1) ).某方法(...); // 验证是否调用了某方法 8. 测试运行与整合
8.1 在本地IDE里运行
右键单个测试类或测试方法 - Run或者在项目主目录运行 mvn test / gradle test
8.2 与持续集成(CI)整合
在 Jenkins、GitLab CI、GitHub Actions 等环境里一般只要执行 mvn test 或 gradle test 就可以跑所有测试用例。如果测试全部通过就说明代码基本没问题如果测试挂了说明你这次提交的改动有Bug或者破坏了原有逻辑。 9. 流程小结简版“使用指南” 新手首次写单元测试 在 src/test/java 下创建和源代码同包路径的测试类XXXTest.java。在类里加 Test 注解的方法里面写 Assertions.assertXXX(...)。右键运行看输出是否通过。 要测Service逻辑但不想连数据库 在测试类上写 ExtendWith(MockitoExtension.class)
public class MyServiceTest {Mockprivate MyRepository myRepository;InjectMocksprivate MyService myService;...
}用 Mockito.when(...) 来模拟依赖。用 assertEquals(...) 来判断结果。 要测Service逻辑并用Spring上下文 在测试类上加 SpringBootTest。注入 ServiceAutowired private MyService myService;如果你不想真的连数据库那就用 MockBean MyRepository myRepository; 要测Controller 用 WebMvcTest(MyController.class) MockBean MyService myService; MockMvc 做单元测试速度较快或者用 SpringBootTest(webEnvironment ... ) TestRestTemplate 做近似真实的集成测试。 10. 其他常见问题
测试和生产环境的配置冲突了怎么办 可以在 application-test.yml 里放测试专用配置然后在测试时用 spring.profiles.activetest。 需要数据库的测试怎么办 可以用DataJpaTest内存数据库比如 H2只测JPA相关逻辑不影响真数据库。 想看覆盖率怎么办 可以集成 Jacoco 插件跑 mvn test 后生成覆盖率报告看你的测试是不是覆盖到了主要逻辑。 测试很慢怎么办 如果你的逻辑不是必须要Spring就尽量用纯Mock不用 SpringBootTest。如果只是测Controller就用 WebMvcTest不要启动全部。