后端单元测试
Service 层测试
使用 Mockito Mock 依赖,聚焦业务逻辑验证。
基本结构
java
@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {
@InjectMocks
private UserServiceImpl userService;
@Mock
private UserMapper userMapper;
@Mock
private RoleService roleService;
@Test
void getUserById_whenUserExists_shouldReturnUser() {
// Given
Long userId = 1L;
User mockUser = new User();
mockUser.setId(userId);
mockUser.setUsername("admin");
when(userMapper.selectById(userId)).thenReturn(mockUser);
// When
User result = userService.getUserById(userId);
// Then
assertThat(result).isNotNull();
assertThat(result.getUsername()).isEqualTo("admin");
verify(userMapper, times(1)).selectById(userId);
}
@Test
void getUserById_whenUserNotExists_shouldThrowException() {
// Given
Long userId = 999L;
when(userMapper.selectById(userId)).thenReturn(null);
// When & Then
assertThatThrownBy(() -> userService.getUserById(userId))
.isInstanceOf(BusinessException.class)
.hasMessage("用户不存在");
}
}测试要点
| 要点 | 说明 |
|---|---|
| Mock 所有依赖 | Service 的 Mapper 和其他依赖全部 Mock |
| 验证调用次数 | 使用 verify 确认方法调用次数 |
| Given-When-Then | 按准备-执行-断言三段式组织 |
| 测试异常 | 使用 assertThatThrownBy 验证异常 |
Mapper 层测试
使用 @MybatisTest 进行 Mapper 层集成测试:
java
@MybatisTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void selectPage_byUsername_shouldReturnMatchedUsers() {
// Given
UserQueryDTO query = new UserQueryDTO();
query.setPageNum(1);
query.setPageSize(10);
query.setUsername("admin");
// When
List<User> result = userMapper.selectPage(query);
// Then
assertThat(result).isNotEmpty();
assertThat(result.get(0).getUsername()).contains("admin");
}
@Test
void insert_whenUserIsValid_shouldReturnGeneratedId() {
// Given
User user = new User();
user.setUsername("testuser");
user.setTenantId(1L);
// When
int result = userMapper.insert(user);
// Then
assertThat(result).isEqualTo(1);
assertThat(user.getId()).isNotNull();
}
}Controller 层测试
使用 @WebMvcTest 测试 Controller:
java
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
void getUserById_whenUserExists_shouldReturn200() throws Exception {
// Given
User mockUser = new User();
mockUser.setId(1L);
mockUser.setUsername("admin");
when(userService.getUserById(1L)).thenReturn(mockUser);
// When & Then
mockMvc.perform(get("/system/user/1")
.header("Authorization", "Bearer test-token"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.username").value("admin"));
}
}测试数据管理
| 方式 | 说明 | 适用场景 |
|---|---|---|
@BeforeEach 构造 | 每个测试前构造数据 | 简单对象 |
| 测试工厂方法 | 封装数据构造 | 复杂对象 |
| Flyway 测试脚本 | 数据库初始化数据 | Mapper 层测试 |
常用断言
java
// AssertJ 流式断言
assertThat(result).isNotNull();
assertThat(result).hasSize(3);
assertThat(result).extracting(User::getUsername)
.containsExactly("admin", "test", "guest");
// 异常断言
assertThatThrownBy(() -> service.delete(null))
.isInstanceOf(IllegalArgumentException.class);
// 集合断言
assertThat(list).anyMatch(user -> user.getStatus() == 1);