Skip to content

后端单元测试基础

Forge 后端测试基于 JUnit 5 + Mockito。你需要掌握 Service 层测试(Mock Mapper)和 Controller 层测试(MockMvc)。

依赖

测试依赖在 forge-starter-test 或各模块 pom.xml 中已配置:

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

Service 层测试

Service 层测试 Mock Mapper,不连接数据库:

java
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {

    @Mock
    private OrderMapper orderMapper;

    @InjectMocks
    private OrderServiceImpl orderService;

    @Test
    void shouldCreateOrderSuccessfully() {
        // 准备
        OrderDTO dto = new OrderDTO();
        dto.setOrderNo("ORD001");
        dto.setAmount(10000L);
        dto.setStatus("0");

        when(orderMapper.selectByOrderNo("ORD001")).thenReturn(null);
        when(orderMapper.insert(any(OrderEntity.class))).thenReturn(1);

        // 执行
        OrderVO result = orderService.create(dto);

        // 验证
        assertNotNull(result);
        assertEquals("ORD001", result.getOrderNo());
        verify(orderMapper).insert(any(OrderEntity.class));
    }

    @Test
    void shouldThrowWhenOrderNoExists() {
        OrderDTO dto = new OrderDTO();
        dto.setOrderNo("ORD001");

        OrderEntity existing = new OrderEntity();
        existing.setOrderNo("ORD001");
        when(orderMapper.selectByOrderNo("ORD001")).thenReturn(existing);

        // 验证抛出异常
        BusinessException ex = assertThrows(
            BusinessException.class,
            () -> orderService.create(dto)
        );
        assertEquals("订单号已存在", ex.getMessage());
    }
}

Controller 层测试

Controller 层测试使用 MockMvc:

java
@SpringBootTest
@AutoConfigureMockMvc
class OrderControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private OrderService orderService;

    @Test
    void shouldReturnPageResult() throws Exception {
        // 准备
        PageResult<OrderVO> pageResult = new PageResult<>();
        pageResult.setTotal(1L);
        pageResult.setList(List.of(new OrderVO()));

        when(orderService.page(any(), eq(1), eq(10)))
            .thenReturn(pageResult);

        // 执行
        mockMvc.perform(get("/biz/order/page")
                .param("pageNum", "1")
                .param("pageSize", "10")
                .header("Authorization", "Bearer test-token"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(200))
            .andExpect(jsonPath("$.data.total").value(1));
    }

    @Test
    void shouldReturn400WhenOrderNoBlank() throws Exception {
        OrderDTO dto = new OrderDTO();
        // orderNo 为空

        mockMvc.perform(post("/biz/order")
                .contentType(MediaType.APPLICATION_JSON)
                .content("{}"))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.code").value(400));
    }
}

测试命名

使用 should + 期望结果 + When + 条件 格式:

java
@Test
void shouldThrowWhenAmountIsZero() { ... }

@Test
void shouldReturnOrderWhenIdExists() { ... }

@Test
void shouldReturnEmptyWhenNoData() { ... }

测试结构(AAA 模式)

java
@Test
void shouldUpdateOrderStatus() {
    // Arrange(准备)
    OrderEntity entity = new OrderEntity();
    entity.setId(1L);
    entity.setStatus("0");
    when(orderMapper.selectById(1L)).thenReturn(entity);

    // Act(执行)
    orderService.updateStatus(1L, "1");

    // Assert(验证)
    verify(orderMapper).updateById(argThat(e -> "1".equals(e.getStatus())));
}

运行测试

bash
# 运行全部测试
cd forge-server && mvn test

# 运行指定类
mvn test -Dtest=OrderServiceTest

# 运行指定方法
mvn test -Dtest=OrderServiceTest#shouldCreateOrderSuccessfully

测试原则

原则说明
独立性测试之间不依赖执行顺序
可重复多次执行结果一致
快速单元测试不连接数据库
只测公共方法不测试 private 方法
Mock 外部依赖Service 测试 Mock Mapper

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