我如何使用jUnit对Servlet过滤器进行单元测试?

49

我已经实现了doFilter()方法。如何使用jUnit正确地覆盖Filter?

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
        throws java.io.IOException, javax.servlet.ServletException
{
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    String currentURL = request.getRequestURI();

    if (!currentURL.equals("/maintenance.jsp") && modeService.getOnline())
    {
        response.sendRedirect("/maintenance.jsp");
    }
    filterChain.doFilter(servletRequest, servletResponse);
}

1
你显然拥有一个 Web 应用程序。JUnit 本身不处理 Web,但还有其他工具。例如,你如何检查你的 Servlet? - Mark Bramnik
你的意思是要使用GUI测试吗? - Pilot
3个回答

42

ServletRequestServletResponseFilterChain都是接口,所以你可以很容易地手动创建测试桩,或使用模拟框架来创建。

使模拟对象可配置,以便您可以为getRequestURI()准备预设响应,并查询ServletResponse以断言是否已调用sendRedirect。

注入一个模拟的ModeService。

通过将模拟的ServletRequestServletResponseFilterChain作为参数传递给doFilter来调用它。

@Test
public void testSomethingAboutDoFilter() {
    MyFilter filterUnderTest = new MyFilter();
    filterUnderTest.setModeService(new MockModeService(ModeService.ONLINE));
    MockFilterChain mockChain = new MockFilterChain();
    MockServletRequest req = new MockServletRequest("/maintenance.jsp");
    MockServletResponse rsp = new MockServletResponse();

    filterUnderTest.doFilter(req, rsp, mockChain);

    assertEquals("/maintenance.jsp",rsp.getLastRedirect());
}

实际上,您需要将设置移动到 @Before setUp() 方法中,并编写更多的 @Test 方法来覆盖每个可能的执行路径。

... 并且您可能会使用像 JMock 或 Mockito 这样的模拟框架来创建模拟,而不是这里使用的假设的 MockModeService 等。

这是一种单元测试方法,而不是集成测试。您只测试单元本身(以及测试代码)。


1
FYI:您不需要注入FilterChain实例——它是doFilter方法的参数。我已经更改了示例以反映这一点。 - Sean Reilly
1
现在只有MockHttpServletRequestMockHttpServletResponse - Eugene

26

如果您正在使用Spring,则它有自己的模拟对象:

  • org.springframework.mock.web.MockFilterChain
  • org.springframework.mock.web.MockHttpServletRequest
  • org.springframework.mock.web.MockHttpServletResponse

例如,您可以添加头信息并设置测试Uri。

因此,您的测试可以像这样:

@RunWith(MockitoJUnitRunner.class)
public class TokenAuthenticationFilterTest {

    private static final String token = "260bce87-6be9-4897-add7-b3b675952538";
    private static final String testUri = "/testUri";

    @Mock
    private SecurityService securityService;

    @InjectMocks
    private TokenAuthenticationFilter tokenAuthenticationFilter = new TokenAuthenticationFilter();

    @Test
    public void testDoFilterInternalPositiveScenarioWhenTokenIsInHeader() throws ServletException, IOException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.addHeader(TOKEN, token);
        request.setRequestURI(testUri);
        MockHttpServletResponse response = new MockHttpServletResponse();
        MockFilterChain filterChain = new MockFilterChain();
        when(securityService.doesExistToken(token)).thenReturn(true);
        tokenAuthenticationFilter.doFilterInternal(request, response, filterChain);
        assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value());
    }
}

23

您可以使用一个mocking框架来mock上述的HttpServletRequestHttpServletResponseFilterChain对象及其行为。根据框架的不同,您可以有一定的功能来验证在执行代码期间是否对mocked对象采取了正确的动作。

例如,当使用Mockito mock框架时,可以使用以下测试用例来测试提供的doFilter()方法:

@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 getRequestURI() response
    when(httpServletRequest.getRequestURI()).thenReturn("/otherurl.jsp");

    MaintenanceFilter maintenanceFilter = new MaintenanceFilter();
    maintenanceFilter.doFilter(httpServletRequest, httpServletResponse,
            filterChain);

    // verify if a sendRedirect() was performed with the expected value
    verify(httpServletResponse).sendRedirect("/maintenance.jsp");
}

1
这是一个很好的例子,也是我认为最好的方法,但使用Mockito来限定静态方法,甚至展示导入以帮助新手更好地理解会使它更好。 - absmiths

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接