How do I unit test a Servlet Filter with jUnit? ServletRequest, ServletResponse, FilterChain

Viewed 4291

How to properly cover Filter with JUnit?

@SlingFilter(order = -700, scope = SlingFilterScope.REQUEST)
public class LoggingFilter implements Filter {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
            final FilterChain filterChain) throws IOException, ServletException {

        final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
        logger.debug("request for {}, with selector {}", slingRequest
                .getRequestPathInfo().getResourcePath(), slingRequest
                .getRequestPathInfo().getSelectorString());

        filterChain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) {}

    @Override
    public void destroy() {}

}
3 Answers

You can use below code for your testing with Junit-5

@ExtendWith(MockitoExtension.class)
public class LoggingFilterTest{
    
    @InjectMocks
    private LoggingFilter loggingFilter;
    
    @Mock
    private ServletRequest request
    
    @Mock
    private ServletResponse response
    
    @Mock
    private FilterChain filterChain
    
    @Mock
    private RequestPathInfo requestPathInfo;
    
    @Test
    public void testDoFilter() throws IOException, ServletException{
    
        Mockito.when(request.getResourcePath()).thenReturn(requestPathInfo);
        Mockito.when(requestPathInfo.getResourcePath()).thenReturn("/testPath", "selectorString");
        Mockito.doNothing().when(filterChain).doFilter(Mockito.eq(request), Mockito.eq(response));
        
        loggingFilter.doFilter(request, response, filterChain);
        
        Mockito.verify(filterChain, times(1)).doFilter(Mockito.eq(request), Mockito.eq(response));
    }
}

If you are using junit4 then change @ExtendWith(MockitoExtension.class) to @RunWith(MockitoJUnitRunner.class)

Invoke doFilter passing the mock ServletRequest, ServletResponse and FilterChain as its parameters.

@Test
public void testDoFilter() {
    LoggingFilter filterUnderTest = new LoggingFilter();    
    MockFilterChain mockChain = new MockFilterChain();
    MockServletRequest req = new MockServletRequest("/test.jsp");
    MockServletResponse rsp = new MockServletResponse();

    filterUnderTest.doFilter(req, rsp, mockChain);

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

In practice, you'll want to move the setup into an @Before setUp() method, and write more @Test methods to cover every possible execution path.

And you'd probably use a mocking framework like JMock or Mockito to create mocks, rather than the hypothetical MockModeService etc. I've used here.

This is a unit testing approach, as opposed to an integration test. You are only exercising the unit under test (and the test code).

If you use AEM Mocks with Junit5, then it could look something like this.

@ExtendWith(AemContextExtension.class)
class SimpleFilterTest {

    private static final AemContext context =  new AemContext(ResourceResolverType.RESOURCERESOLVER_MOCK);

    private static final String RESOURCE_PATH = "/content/test";
    private static final String SELECTOR_STRING = "selectors";

    private static SimpleFilter simpleFilter;
    private static FilterChain filterChain;

    @BeforeAll
    static void setup() {
        simpleFilter = context.registerService(SimpleFilter.class, new SimpleFilter());
        filterChain = context.registerService(FilterChain.class, new MockFilterChain());
    }

    @Test
    @DisplayName("GIVEN the request, WHEN is executed, THEN request should be filtered and contain corresponding header")
    void testDoFilter() throws ServletException, IOException {
        context.requestPathInfo().setResourcePath(RESOURCE_PATH);
        context.requestPathInfo().setSelectorString(SELECTOR_STRING);

        simpleFilter.doFilter(context.request(), context.response(), filterChain);

        assertEquals("true", context.response().getHeader("filtered"));
    }

}

Mock

public class MockFilterChain implements FilterChain {

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response) throws IOException, ServletException {
        // do nothing
    }

}

Some simple filter

@Component(service = Filter.class)
@SlingServletFilter(scope = SlingServletFilterScope.REQUEST)
@ServiceRanking(-700)
public class SimpleFilter implements Filter {

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException {
        final SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
        log.debug("request for {}, with selector {}", slingRequest.getRequestPathInfo().getResourcePath(),
          slingRequest.getRequestPathInfo().getSelectorString());

        final SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;
        slingResponse.setHeader("filtered", "true");
        filterChain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void destroy() {
    }

}
Related