You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Spring Boot与Mockito模拟JSON文档完成方法测试

Spring Boot结合Mockito测试JSON列表入参处理方法的实操方案

测试核心思路

这类方法的测试不需要启动完整Spring容器,也不用Mock HTTP请求,核心覆盖三个逻辑节点即可:入参JSON和Java实体的映射正确性、业务处理逻辑的分支覆盖、返回结果符合预期。Mockito的作用是隔离数据库、远程调用这类外部依赖,JSON测试文档不需要做Mock,直接构造真实的结构化字符串即可,绝对不要Mock JSON解析器的核心方法,否则等于跳过解析逻辑白测。


具体操作步骤

1. 测试类基础配置

用JUnit5+Mockito做纯单元测试即可,测试类加@ExtendWith(MockitoExtension.class)注解,不需要加载整个Spring上下文,跑测速度快。如果是测Controller层的接口入参,再加@WebMvcTest做切片测试就行。
先举个待测试的业务方法示例,方便对照:

// 待测试方法:接收集合格式的JSON字符串,解析后处理数据,返回VO列表
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository; // 数据库操作依赖,测试时需要Mock
    @Autowired
    private ObjectMapper objectMapper; // 框架自带JSON解析器,不用Mock

    public List<UserVO> batchImportUsers(String jsonDoc) throws JsonProcessingException {
        // 第一步:解析JSON为DTO列表
        List<UserCreateDTO> dtoList = objectMapper.readValue(jsonDoc, new TypeReference<>() {});
        // 第二步:字段校验
        validateUserDtoList(dtoList);
        // 第三步:转换为实体存库
        List<User> entityList = dtoList.stream().map(User::new).toList();
        List<User> savedUsers = userRepository.saveAll(entityList);
        // 第四步:转换为VO返回
        return savedUsers.stream().map(UserVO::new).toList();
    }
}

2. 构造测试用JSON文档

两种靠谱的构造方式,根据JSON复杂度选:

  • 短JSON直接用Java文本块写常量,覆盖正常、异常、边界三类场景
  • 结构复杂的长JSON,存在src/test/resources/testdata目录下的json文件里,测试时读成字符串就行,比硬编码好维护

示例测试JSON常量:

// 正常场景:2个结构合法的用户对象
private static final String VALID_BATCH_JSON = """
[
  {"userId":1,"userName":"zhangsan","age":25,"email":"zhangsan@corp.com"},
  {"userId":2,"userName":"lisi","age":30,"email":"lisi@corp.com"}
]
""";
// 异常场景:第二个对象缺必填的userName字段
private static final String MISSING_REQUIRED_FIELD_JSON = """
[
  {"userId":1,"userName":"zhangsan","age":25,"email":"zhangsan@corp.com"},
  {"userId":2,"age":30,"email":"lisi@corp.com"}
]
""";
// 边界场景:空数组
private static final String EMPTY_ARRAY_JSON = "[]";
// 异常场景:非法JSON格式
private static final String INVALID_JSON_STR = "[{\"userId\":1,userName:'zhangsan'}]";

3. 用Mockito隔离外部依赖

只Mock和业务逻辑无关的外部IO依赖,核心的解析、计算、校验逻辑不要Mock:

  • 用@Mock标记数据库操作、远程调用这类外部依赖组件
  • 用@InjectMocks标记待测试的服务类,Mockito会自动把Mock的依赖注入进去
  • 对Mock组件做行为打桩,比如存库操作直接返回预设的实体,不要真的连库

示例正常场景测试代码:

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    @InjectMocks
    private UserService userService;
    @Mock
    private UserRepository userRepository;
    // 用和生产环境一致的ObjectMapper配置
    private final ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();

    @Test
    void batchImportUsers_validJson_returnCorrectVoList() throws JsonProcessingException {
        // 打桩:存库操作直接返回构造好的实体,不真的访问数据库
        List<User> mockSavedEntities = objectMapper.readValue(VALID_BATCH_JSON, new TypeReference<>() {});
        when(userRepository.saveAll(anyList())).thenReturn(mockSavedEntities);

        // 执行待测试方法
        List<UserVO> result = userService.batchImportUsers(VALID_BATCH_JSON);

        // 结果断言
        assertEquals(2, result.size());
        assertEquals("zhangsan", result.get(0).getUserName());
        assertEquals("lisi", result.get(1).getUserName());
        // 行为验证:确认存库方法被正确调用,参数长度符合预期
        verify(userRepository, times(1)).saveAll(argThat(list -> list.size() == 2));
    }
}

4. 覆盖异常与边界场景

不要只测正常流程,要把各环节的异常分支都覆盖到:

@Test
void batchImportUsers_missingRequiredField_throwValidationException() {
    // 断言缺字段时抛出预期的校验异常
    assertThrows(ParamCheckException.class, () -> userService.batchImportUsers(MISSING_REQUIRED_FIELD_JSON));
    // 校验不通过时存库方法不应该被调用
    verify(userRepository, never()).saveAll(anyList());
}

@Test
void batchImportUsers_emptyArray_returnEmptyList() throws JsonProcessingException {
    List<UserVO> result = userService.batchImportUsers(EMPTY_ARRAY_JSON);
    assertTrue(result.isEmpty());
    verify(userRepository, times(1)).saveAll(argThat(List::isEmpty));
}

@Test
void batchImportUsers_invalidJson_throwParseException() {
    assertThrows(JsonProcessingException.class, () -> userService.batchImportUsers(INVALID_JSON_STR));
    verify(userRepository, never()).saveAll(anyList());
}

常见踩坑

  • 别为了追求“单元测试要全Mock”把ObjectMapper、字段校验器这类核心组件也Mock掉,这么做会跳过JSON字段映射、类型转换这类容易出问题的逻辑,测试完全没有参考价值
  • 如果是测Controller层的JSON入参,用@WebMvcTest+MockMvc的时候,直接把构造好的JSON字符串当requestBody传入即可,只需要Mock Service层的返回,就能同时验证参数解析、响应序列化的逻辑
  • 测试用的JSON结构要和生产环境真实入参保持一致,不要随便简化字段、改字段名,否则上线才发现字段名不匹配、类型不兼容就会出线上故障

内容的提问来源于stack exchange,提问作者Elmo Pro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:39:54