ContentCachingResponseWrapper.getContentAsByteArray() 在使用 MockHttpServletResponse 进行测试时为空

ContentCachingResponseWrapper.getContentAsByteArray() is empty when testing with MockHttpServletResponse

我有一个过滤器,用于将每个请求的一些信息记录到 Spring 启动应用程序。我需要从 body 中提取其中的一些信息。这本身不是问题,但为此我使用了 ContentCachingResponseWrapper,这搞乱了我的单元测试。

这是我的过滤器的简化版本:

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    try {
        var wrappedResponse = response instanceof ContentCachingResponseWrapper ? (ContentCachingResponseWrapper) response : new ContentCachingResponseWrapper(response);
        filterChain.doFilter(request, wrappedResponse);
    } finally {        
        System.out.println("Response body: " + new String(wrappedResponse.getContentAsByteArray()));
        wrappedResponse.copyBodyToResponse();
    }
}

这是我测试的简化版本:

    void myTest() throws ServletException, IOException {
        final String body = "This is a body that my service might return.";
        var testResp = new MockHttpServletResponse();
        testResp.getWriter().print(body);
        testResp.getWriter().flush();
        testResp.setContentLength(body.length());

        myFilter.doFilterInternal(Mockito.mock(HttpServletRequest.class), testResp, Mockito.mock(FilterChain.class));
    }

问题是,当 运行 我的测试时,wrappedResponse.getContentAsByteArray() returns 一个空数组。

您的代码有两处错误

  1. 您的过滤器没有将响应包装在 ContentCachingResponseWrapper
  2. 您在对基础响应进行包装之前编写响应,因此 ContentCachingResponseWrapper 没有更改缓存响应。
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    try {
        var wrappedResponse = response instanceof ContentCachingResponseWrapper ? (ContentCachingResponseWrapper) response : new ContentCachingResponseWrapper(response);
        filterChain.doFilter(request, wrappedResponse);
    } finally {        
        System.out.println("Response body: " + new String(wrappedResponse.getContentAsByteArray()));
        wrappedResponse.copyBodyToResponse();
    }
}

现在,响应将被包装在 FilterChain 后面写的响应的包装器中。这也是您可以通过模拟 FilterChain 并在答案中写入响应来在测试用例中利用的东西。

void myTest() throws ServletException, IOException {
  var body = "This is a body that my service might return.";
  var req = new MockHttpServletRequest();
  var res = new MockHttpServletResponse();
  var mockChain = Mockito.mock(FilterChain.class);
  Mockito.when(mockChain.doFilter(any(), any())
    .thenAnswer((it -> {
      var response = it.getArgument(1, HttpServletResponse.class);
      response.getWriter().print(body);
      response.getWriter().flush();
      response.setContentLength(body.length());
      return null;      
     });
   myFilter.doFilterInternal(req, res, mockChain);
}

按照这些思路应该可以解决问题。