【问题标题】:How to Junit test servlet filter which has specific response如何Junit测试具有特定响应的servlet过滤器
【发布时间】:2012-11-13 17:21:58
【问题描述】:

对此代码进行单元测试的最佳方法是什么?我需要为 httpResponse 建立一个一致的检查,当条件为真时 sendError() 。提前致谢!

编辑:不幸的是,这个过滤器不支持 Spring MVC,所以我的选择是有限的。

    public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain filterchain) throws IOException, ServletException {

    String ipAddress = request.getRemoteAddr();
    if( SomeParameterCheckingFunction ((request)) ) {
        logger.error("Error detected! Time: " + new Date().toString() + ", Originating IP: " + ipAddress);
        if (response instanceof HttpServletResponse){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN,"You are not allowed to access the server!");
        }
    }
    filterchain.doFilter(request, response);
}

【问题讨论】:

    标签: testing servlets junit mockito servlet-filters


    【解决方案1】:

    例如,当使用 Mockito 模拟框架时,提供的 doFilter() 方法可以使用以下测试用例进行 JUnit 测试:

    @Test
    public void testDoFilter() throws IOException, ServletException {
        // create the objects to be mocked
        HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
        HttpServletResponse httpServletResponse = mock(HttpServletResponse.class);
        FilterChain filterChain = mock(FilterChain.class);
        // mock the getRemoteAddr() response
        when(httpServletRequest.getRemoteAddr()).thenReturn("198.252.206.16");
    
        AccessFilter accessFilter = new AccessFilter();
        accessFilter.doFilter(httpServletRequest, httpServletResponse,
                filterChain);
    
        // verify if a sendError() was performed with the expected values
        verify(httpServletResponse).sendError(HttpServletResponse.SC_FORBIDDEN,
                "You are not allowed to access the server!");
    }
    

    【讨论】:

    • 你为什么嘲笑getRemoteAddr()。好像没这个必要。
    【解决方案2】:

    一种选择可能是将SomeParameterCheckingFunction 的定义从过滤器本身中提取出来(并在过滤器中使用它)。然后,您可以单独对该逻辑进行单元测试。但是,我不确定这对你来说是否足够。

    另一个选项可能是使用 Mockito 测试过滤器类。这将涉及一些劳动,模拟请求、响应、过滤器链、日志记录等。之后您应该能够验证 response.sendError() 调用类似这样(假设静态导入 Mockito.* 方法):

    //setup mock with concrete class for instanceof check
    ServletResponse resp = mock(HttpServletResponse.class);
    
    //define behavior for desired method with void return type.  this appears optional for mocks.
    //doNothing().when(resp).sendError(anyInt(), anyString());
    
    //your test here, passing in resp       
    if(resp instanceof HttpServletResponse) resp.sendError(400, "blah");
    
    //verify method was called      
    verify(resp).sendError(400, "blah");
    

    可能有也可能没有办法用HttpUnit 测试您的过滤器,但这可能无法满足您对适当单元测试的需求。

    【讨论】:

    • 我已经对 SomeParameterCheckingFunction() 进行了另一个单元测试,因此我可以使用 Mockito 轻松模拟它。能够验证 response.sendError() 将是一个理想的测试用例,但我完全不知道如何做到这一点。你能告诉我在调用过滤器后如何在junit中验证response.sendError()的技术吗? Tnx!
    【解决方案3】:

    2018 年 2 月更新:OpenBrace Limited has closed down,不再支持其 ObMimic 产品。

    这是一个示例,说明如何使用我的 ObMimic Servlet API 容器外测试替身库测试“sendError”。

    首先,关于给定 doFilter 代码的一些注意事项:

    • "((request))" 可能只是“(request)”。

    • 没有显示“记录器”的性质,所以我暂时忽略了它,因为它无关紧要。

    • 给定的过滤器将检查非 HTTP 请求,但如果它们被拒绝,则会写入通常的日志消息,但随后会跳过 sendError 并改为执行正常处理。根据您尝试执行的操作,您可能希望预先拒绝非 HTTP 请求,或者根本不检查它们,或者使用其他类型的拒绝来代替“sendError”。现在我假设我们只对如何在 HTTP 请求上测试“sendError”感兴趣。

    • 下面的示例测试表明,即使调用了 sendError,处理仍然会直接进入正常的“doFilter”调用。假设这不是故意的,“sendError”应该跟一个“return”,或者“doFilter”应该在一个“else”子句中。

    对于下面的示例代码:

    • 我使用 JUnit 作为测试框架,但它也可以是 TestNG 或其他任何东西。

    • 对于“SomeParameterCheckingFunction”,我刚刚使用了一个如果远程 IP 地址为“1.1.1.1”则返回 true。

    • "sendError" 生成显示给定消息的 HTML 响应。对于这个例子,我只是检查响应正文是否包含给定的消息。如果您想检查传递给“sendError”的参数值,ObMimic 的“专业版”有一个“历史”功能可以让您这样做。或者您可以在单独的响应实例上执行“sendError”,然后检查响应正文是否完全匹配。

    不管怎样,下面是示例代码:

    package com.openbrace.experiments.examplecode.stackoverflow13365536;
    
    import static org.junit.Assert.*;
    import com.openbrace.experiments.examplecode.stackoverflow13365536.YourFilter;
    import com.openbrace.obmimic.mimic.servlet.FilterChainMimic;
    import com.openbrace.obmimic.mimic.servlet.FilterConfigMimic;
    import com.openbrace.obmimic.mimic.servlet.ServletMimic;
    import com.openbrace.obmimic.mimic.servlet.http.HttpServletRequestMimic;
    import com.openbrace.obmimic.mimic.servlet.http.HttpServletResponseMimic;
    import com.openbrace.obmimic.support.servlet.EndPoint;
    import org.hamcrest.CoreMatchers;
    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import javax.servlet.FilterChain;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    /**
     * Example tests for {@link YourFilter#doFilter(ServletRequest, ServletResponse,
     * FilterChain)}.
     *
     * @author Mike Kaufman, OpenBrace Limited
     */
    public class YourFilterTest {
    
        /** The filter to be tested by this instance's test. */
        private YourFilter filter;
    
        /** The "mimic" request to be used in this instance's test. */
        private HttpServletRequestMimic request;
    
        /** The "mimic" response to be used in this instance's test. */
        private HttpServletResponseMimic response;
    
        /** The filter chain to be used in this instance's test. */
        private FilterChainMimic filterChain;
    
        /**
         * Set up for this instance's test by creating an initialized filter and a
         * request, response and filter chain for use in the test.
         *
         * @throws ServletException if the filter's init method throws such an
         *     exception.
         */
        @Before
        public void setUp() throws ServletException {
            /*
             * Note that for this example's simple filter and tests:
             * - We don't need anything particular in the filter's FilterConfig.
             * - We don't need any particular ServletContext, so we can leave
             *   ObMimic to use its default ServletContext throughout.
             * - We don't need to retain any references to the filter's FilterConfig
             *   or ServletContext.
             * - We can use a FilterChainMimic with default values as the filter
             *   chain. This has a ServletMimic as the next thing in the chain, and
             *   ServletMimic keeps a count of the calls to its service method, so
             *   we can use this to check whether the filter chain's doFilter has
             *   been invoked.
             */
            filter = new YourFilter();
            filter.init(new FilterConfigMimic());
            request = new HttpServletRequestMimic();
            response = new HttpServletResponseMimic();
            filterChain = new FilterChainMimic();
        }
    
        /**
         * Test the doFilter method with an example request for which
         * SomeParameterCheckingFunction returns true (so that the FORBIDDEN
         * response should result).
         *
         * @throws ServletException if the servlet throws such an exception.
         * @throws IOException if the servlet throws such an exception.
         */
        @Test
        public void testYourFilterWithForbiddenRequest()
                throws ServletException, IOException {
    
            // Configure the request so that SomeParameterCheckingFunction will
            // return true, which for purposes of this example is triggered by a
            // particular "remote address".
            request.getMimicState().setRemoteEndPoint(
                new EndPoint(null, "1.1.1.1", null));
    
            // Invoke the doFilter method.
            filter.doFilter(request, response, filterChain);
    
            // Check that the response is now FORBIDDEN, and that its HTML content
            // does include the expected text message.
            int responseCode = response.getMimicState().getHttpStatusCode();
            String responseBody = response.getMimicState().getBodyContentAsString();
            String expectedMessage = "You are not allowed to access the server!";
            assertEquals("Response has incorrect status code",
                HttpServletResponse.SC_FORBIDDEN, responseCode);
            Assert.assertThat("FORBIDDEN response does not include expected message",
                responseBody, CoreMatchers.containsString(expectedMessage));
    
            // Check that the filter chain was not invoked. As we've used a
            // FilterChainMimic with default values, its "target" is a ServletMimic,
            // so we can just check if there have been any calls to that Servlet.
            ServletMimic targetServlet
                = (ServletMimic) filterChain.getMimicState().getTarget();
            boolean filterChainInvoked
                = targetServlet.getMimicState().getServiceCount() > 0;
            assertFalse("FORBIDDEN response but filterChain.doFilter still called",
                filterChainInvoked);
    
        }
    
    }
    

    如果您想尝试一下,可以在ObMimic 网站上免费下载 ObMimic 的完整详细信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-09-09
      • 2015-08-05
      • 2012-11-10
      • 2011-11-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-20
      • 1970-01-01
      相关资源
      最近更新 更多