【问题标题】:ContentCachingResponseWrapper.getContentAsByteArray() is empty when testing with MockHttpServletResponse使用 MockHttpServletResponse 进行测试时,ContentCachingResponseWrapper.getContentAsByteArray() 为空
【发布时间】:2021-12-17 01:38:48
【问题描述】:

我有一个过滤器,用于记录对 Spring Boot 应用程序的每个请求的一些信息。我需要从身体中提取其中一些信息。这本身不是问题,但为此我使用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() 返回一个空数组。

【问题讨论】:

  • 为什么要这样做?响应没有被包装,至少你的过滤器没有包装任何东西。所以不确定你认为它是如何工作的。
  • @M.Deinum finally 块中的第一行不是将响应包装在 ContentCachingResponseWrapper 中吗?
  • 不,它检查它是否是该类的实例并在事后包装。响应应在调用filterchain.doFilter 之前包装并作为响应传递。然后应在 finally 块中使用包装的响应。
  • 谢谢。我可能在调试时移动了一些东西,因为这在之前(不测试时)有效。我现在已经更改了它,并且在测试期间行为仍然相同。
  • 这也是意料之中的。当您在ContentCachingResponseWrapper 有时间拦截并缓存它之前编写响应时。

标签: java spring-boot unit-testing junit servlet-filters


【解决方案1】:

您的代码有 2 个问题

  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);
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-03
    • 1970-01-01
    • 2014-06-16
    • 1970-01-01
    • 2021-11-03
    • 1970-01-01
    • 2012-09-12
    • 2014-05-03
    相关资源
    最近更新 更多