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

Sling Servlet单元测试返回null 无法正确调用方法排查

问题定位

你的测试跑失败是三个低级错误叠出来的:

  • 校验对象取错了:你Servlet里明明是调用response.setContentType(APPLICATION_JSON)往响应对象写Content-Type头,测试里居然去读req.getContentType()——请求的Content-Type是调用方发请求带的头,你测试里从来没给请求设过这个值,拿到的当然是null。
  • 测试场景根本没走到设响应头的逻辑分支:看你Servlet代码,只有参数解析正常、业务方法调用成功的try块里才会设置响应Content-Type。你现在测试里啥请求参数都没传,Double.parseDouble(request.getParameter("amount"))拿不到值直接抛NumberFormatException,直接进catch块了,catch块里根本没写设Content-Type的逻辑,就算你改对读response的头,这个场景下也拿不到application/json。
  • 断言方法参数顺序写反了:JUnit的assertEquals要求第一个参数传预期值,第二个传代码实际产出的值,你把实际拿到的null放第一个参数位、预期的"application/json"放第二个,才会出现报错里Expected是null、Actual是字符串的反常输出。

另外你测试初始化逻辑写得很乱:同时打了@Mock/@InjectMocks注解,又在setup方法里手动new Servlet、手动从context拿新的request/response对象赋值,会导致注入的依赖和测试里实际用的对象不是同一个,属于埋坑的写法,这次报错不是它直接导致的,但迟早出问题。

修复步骤

1. 补全Servlet异常分支的响应头设置

不管请求处理成功还是失败,都应该给响应设正确的Content-Type,不然接口报错时返回的头不符合预期:

catch (NullPointerException | NumberFormatException exception) {
    LOG.error("{} : Exception when parsing request parameters : ", LOG_STRING, exception);
    responseText = "Error when parsing request parameters";
    response.setContentType(APPLICATION_JSON); // 异常分支也加上头设置
}

2. 重写测试逻辑

  • 删掉冲突的Mock注解,统一用Sling Context构造测试对象
  • 测试正常场景时,先给请求塞必填的三个参数,同时mock掉业务依赖方法的返回值,确保逻辑能走到设响应头的分支
  • 断言时读响应对象的Content-Type,调整断言参数顺序
    修正后的正常场景测试代码:
private MockSlingHttpServletRequest req;
private MockSlingHttpServletResponse res;
private CurrencyExchangeServlet underTest;

// 如果你用的是Sling/AEM测试的@Rule SlingContext context,保留即可
@Before
public void setup() {
    underTest = new CurrencyExchangeServlet();
    // 手动注入Servlet依赖的currencyRatesCalculation mock对象
    underTest.currencyRatesCalculation = Mockito.mock(CurrencyRatesCalculation.class);
    req = context.request();
    res = context.response();
}

@Test
public void doGet_shouldReturnJsonHeaderWhenRequestSuccess() throws IOException {
    // 构造合法请求参数
    req.setParameter("base", "USD");
    req.setParameter("convertTo", "CNY");
    req.setParameter("amount", "100");
    // mock业务方法返回,避免空指针
    Mockito.when(underTest.currencyRatesCalculation.getConvertedAmount(anyString(), anyString(), anyDouble()))
            .thenReturn("{\"convertedAmount\":720.0}");

    underTest.doGet(req, res);
    // 第一个参数传预期值,第二个传实际值,读取response的Content-Type
    assertEquals("application/json", res.getContentType());
}

3. 补充异常场景测试

单独写用例验证参数缺失的异常场景,确保异常分支逻辑符合预期:

@Test
public void doGet_shouldReturnErrorWhenRequestParamInvalid() throws IOException {
    // 不传任何参数,触发异常分支
    underTest.doGet(req, res);
    assertEquals("application/json", res.getContentType());
    assertEquals("Error when parsing request parameters", res.getOutputAsString());
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 21:03:40