Skip to content

后端单元测试

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);

下一步

Forge Admin — 基于 Vue3 + Spring Boot 的企业级后台管理框架