【问题标题】:Testing ServletFilter with Mockito using a response wrapper使用响应包装器使用 Mockito 测试 ServletFilter
【发布时间】:2017-03-17 10:49:09
【问题描述】:

我正在尝试为我编写的过滤器编写单元测试。这听起来很简单,但有一点我不太清楚是哪个设置了 ServletResponse 的状态代码。不幸的是,这很复杂,因为我使用的是 HttpServletResponseWrapper,因为我需要检查响应状态代码(因为我需要覆盖它并返回 200)。作为参考,我返回了一个 IncludedResponse,它确实有一个 getStatusCode() 但由于某种原因在 JDK 源代码中它返回 0?!?无论如何,这是我拥有的过滤器的一部分:

private void handleUploadFilePost(ServletResponse servletResponse, HttpServletResponse httpServletResponse,
    HttpServletRequest httpServletRequest, FilterChain filterChain) throws IOException, ServletException {
    RequestWrapper requestWrapper = new RequestWrapper(httpServletRequest);
    ResponseWrapper responseWrapper = new ResponseWrapper(httpServletResponse);
    String requestBodyTrimmed = getRequestBody(requestWrapper.getInputStream());
    Case caseDetails = requestBodyMappingHelper.mapRequestBody(requestBodyTrimmed, Case.class);
    filterChain.doFilter(requestWrapper, responseWrapper);
    if (responseWrapper.getStatus() >= 400 && responseWrapper.getStatus() <= 599) {
        ...
    }
}

不幸的是,这会在测试时产生问题。如果它是一个简单的 ServletResponse,我会对其进行模拟并执行...

when(myServletResponse).getStatus().thenReturn(...)

但是,由于我正在为状态创建自定义包装器,因此似乎无法设置此局部变量的状态代码,因为它在覆盖 HttpServletResponse setStatus 的包装器中作为 int 保存。

因此,我可以使用 Mockito 或 Powermock 来做我想做的事情吗?当设置 HttpServletResponse 值时,它会覆盖它并将状态存储在我的包装器中,但我无法让它工作。我已经尝试了以下方法:

doAnswer(new Answer() {
    public Object answer(InvocationOnMock invocation) {
        servletResponse.setStatus(400);
        return null; // void method, so return null
    }
}).when(filterChain).doFilter(Mockito.any(ServletRequest.class), Mockito.any(ServletResponse.class));

但这由于某种原因不起作用。测试中的 ServletResponse 没有被模拟,我实际上是在创建它,以便它触发它的 setStatus(),它会被 Wrapper 拦截;

private IncludedResponse servletResponse;

servletResponse = new IncludedResponse();
servletResponse.setProxiedHttpServletResponse(new IncludedResponse());

您能提供的任何帮助将不胜感激。

非常感谢

【问题讨论】:

    标签: java filter mockito


    【解决方案1】:

    when(myServletResponse).getStatus().thenReturn(...)

    正确的是

    when(myServletResponse.getStatus()).thenReturn(...)

    或者换成更一般的形式:

    doReturn(...).when(myServletResponse).getStatus()
    

    这也允许在 void 方法上抛出异常:

    doThrow(new *Exception())when(mock).veoidMethod();
    

    我也不能这样做(doReturn)部分,因为我如何将包装器模拟为局部变量。这就是为什么我需要设置原始的 ServletResponse.setStatus 方法

    如果您应用依赖注入,您可以轻松使用本地模拟。

    【讨论】:

    • 谢谢,我原来的帖子弄错了。但由于上述原因,我不能这样做。
    • 对于您的更新消息,我也不能这样做(doReturn)部分,因为我如何将包装器模拟为局部变量。这就是为什么我需要设置原始的 ServletResponse.setStatus 方法,该方法将触发我的包装器覆盖以设置它的值。否则是的,它会模拟该值,但不会在使用包装器的实际过滤器中被拾取(因为 servletResponse 由于上述原因,没有任何方法可以获取 statusCode ..
    【解决方案2】:

    不知道为什么我以前没有想到这一点。我能做的是创建自己的过滤器链:

    FilterChain filterChain = new FilterChain() {
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
            throws IOException, ServletException {
            if (servletResponse instanceof HttpServletResponse) {
                ((HttpServletResponse) servletResponse).setStatus(400);
            }
        }
    };
    

    这样,当响应返回时,我不需要模拟它或其他任何东西,但我实际上得到了包装器使用的响应对象。比尝试使用模拟(如果可能的话)要容易得多。

    感谢蒂莫西的评论。我会把这个留在这里,以防其他人像我一样有脑残!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多