【问题标题】:Stub chained function call yields NullPointerException存根链式函数调用产生 NullPointerException
【发布时间】:2025-11-28 12:00:01
【问题描述】:

在我的 Spring Boot 项目中,我有测试,我想存根链式函数调用。

要测试的函数调用是:

private String sniffPayload(HttpServletRequest request) throws IOException {
        return request.getReader().lines().collect(Collectors.joining(System.lineSeparator()));
    }

在我的单元测试中,我使用 Mockito 来模拟 HttpServletRequest:

import org.springframework.boot.test.mock.mockito.MockBean;

@MockBean
private HttpServletRequest mockedRequest;

然后,在我的测试函数中:

    @Test
    void testMyFunction() throws Exception {

     // I try to stub the function return 
     // But get NullPointerException at runtime

 when(mockedRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()))).thenReturn("FooBarData");

    ...
    }

当我运行测试时,我得到了 NullPointerException 用于执行存根的代码行 when(mockedRequest.getReader().lines().collect(Collectors.joining(System.lineSeparator()))).thenReturn("FooBarData");

为什么?我怎样才能在存根链接函数返回时摆脱这个NullPointerException

【问题讨论】:

    标签: unit-testing spring-boot mockito


    【解决方案1】:

    Afaik @MockBean 不会创建 deep stub mocks,这意味着不支持链接模拟调用。

    你的问题是mockedRequest.getReader()返回null。

    您可以切换为仅使用 mockito
    (如果您不需要任何自动装配和诸如此类 /
    恕我直言,这在 HttpServletRequest 的情况下似乎不相关)

    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
    


    否则,您必须为每个调用的方法提供不同的模拟。

    Stream stream = Mockito.mock(Stream.class);
    when(stream.collect(Collectors.joining(System.lineSeparator()))).thenReturn("FooBarData");
    
    BufferedReader reader = Mockito.mock(BufferedReader.class);
    when(reader.lines()).thenReturn(stream);
    
    when(mockedRequest.getReader()).thenReturn(reader)
    

    模拟流非常难看,因此您可能想用提供匹配答案的真实流替换该部分。

    例如:

    Stream<String> stream = Stream.of("FooBarData");
    
    when(reader.lines()).thenReturn(stream);
    

    【讨论】:

    • 谢谢,如何replace that part with a real stream
    • 实际上数据是像{"name": "John", "age": 26, "address":"foo", ...}这样的Json格式如果我想在测试中使用这些真实数据,是否需要将每个json键值添加到list?还是应该将整个 json 字符串作为一个元素添加到 list
    • 只要最终结果(只有一个字符串)相同,您如何添加它都无关紧要;)。最后,您应该(已经)知道格式的外观,因此您可以以正确的格式返回它。
    • 是的,我想检查拦截器中有效载荷内的一些字段。但是我刚刚验证我可以将整个json添加到list中,被测代码能够获取json中的每个键值:) 感谢您的帮助。
    • 更新示例实际上有一个Stream 类可以直接从String 中创建Stream。不需要列表的解决方法。