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

实现可测试代码时如何避免过度工程?附JSON处理示例

如何平衡JSON类实例化的可测试性与避免过度工程?

这种场景我太熟悉了——想让代码可测试,但又不想为了每个第三方库类都套一层工厂,搞得代码臃肿不堪。咱们一步步拆解问题,看看有哪些更灵活的方案:

1. 先反思:是否真的需要Mock JSON库的所有细节?

你之前的痛点是不想写真实JSON来测试逻辑,但其实可以换个思路:测试时用极简的合法JSON字符串,而非Mock整个JSONArray/JSONObject。这样既不用引入额外的工厂类,测试也更贴近真实业务场景。

比如你的测试可以改成这样:

@Test 
public void getAttributes_givenResponse_shouldReturnAttributes() { 
    final Response response = mock(Response.class); 
    // 用极简的合法JSON,完全不需要Mock任何JSON类
    when(response.getEntityContentAsString()).thenReturn("[{\"name\":\"test\"}]"); 

    final Map<String, Object> attributes = basicuserModule.getMappedAttributes(mock(Source.class), response); 

    Set<String> expectedUsers = new HashSet<>();
    expectedUsers.add("test");
    assertThat(attributes.get("mappedUsers")).isEqualTo(expectedUsers); 
}

这种方式的好处:

  • 代码保持简洁,没有额外的工厂类开销
  • 测试逻辑和真实业务逻辑完全对齐,避免Mock带来的“测试过了但实际跑不起来”的问题
  • 不用关心JSON库的实现细节,专注验证业务结果

2. 如果确实需要Mock,用适配器而非零散工厂

如果你的业务里经常需要对JSON做复杂操作,或者担心未来更换JSON库的成本,那可以考虑封装JSON操作的适配器,而非给每个JSON类单独写工厂。

比如先定义一个业务聚焦的接口:

public interface UserJsonProcessor {
    Set<String> extractUserNamesFromJson(String jsonArrayStr);
    // 可以添加其他业务相关的JSON操作方法
}

然后用第三方JSON库实现这个接口:

public class DefaultUserJsonProcessor implements UserJsonProcessor {
    @Override
    public Set<String> extractUserNamesFromJson(String jsonArrayStr) {
        JSONArray users = new JSONArray(jsonArrayStr);
        Set<String> names = new HashSet<>();
        for (int i = 0; i < users.length(); i++) {
            names.add(users.getJSONObject(i).getString("name"));
        }
        return names;
    }
}

业务代码就可以简化成:

@Override 
public Map<String, Object> getAttributes(Source source, Response response) { 
    Objects.requireNonNull(response, "response can not be null"); 
    final Map<String, Object> attributes = new HashMap<>(); 
    Set<String> mappedUsers = userJsonProcessor.extractUserNamesFromJson(response.getEntityContentAsString());
    attributes.put("mappedUsers", mappedUsers); 
    return attributes; 
}

测试时只需要Mock这个适配器接口:

@Test 
public void getAttributes_givenResponse_shouldReturnAttributes() { 
    final Response response = mock(Response.class); 
    UserJsonProcessor mockProcessor = mock(UserJsonProcessor.class);
    
    Set<String> testNames = new HashSet<>();
    testNames.add("test");
    when(mockProcessor.extractUserNamesFromJson(anyString())).thenReturn(testNames);

    // 注入mockProcessor到basicuserModule
    final Map<String, Object> attributes = basicuserModule.getMappedAttributes(mock(Source.class), response); 

    assertThat(attributes.get("mappedUsers")).isEqualTo(testNames); 
}

这种方式的优势:

  • 只封装业务需要的操作,不会产生大量零散的工厂类
  • 彻底隔离第三方JSON库的依赖,以后换库只需要修改适配器实现
  • 测试完全聚焦业务逻辑,不用纠结JSON解析的底层细节

3. 什么时候适合用单一工厂类?

如果你的项目里大量直接实例化JSON类,而且确实需要灵活切换JSON库(比如从org.json换到Jackson),那可以考虑写一个统一的JsonFactory,而非每个JSON类单独建工厂:

public interface JsonFactory {
    JSONArray createArray(String json);
    JSONObject createObject(String json);
    // 其他通用创建方法
}

这样既满足了可测试性,又不会产生过多冗余类,便于统一管理。

4. 避免过度工程的核心原则

  • 只封装频繁变化或存在替换风险的依赖:如果项目一直用同一个JSON库,且很少修改JSON解析逻辑,没必要为了测试强行加工厂
  • 测试优先但不牺牲代码简洁性:测试的目的是保障质量,不是为了Mock而Mock。能用真实极简数据测试的,就不要Mock第三方库
  • 和团队统一规范:和团队沟通清楚,不是所有第三方类都需要工厂,只针对那些确实影响测试或可能变更的依赖做封装

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:15:20