You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mockito测试HttpFilter的doFilter方法覆盖率为0%的问题排查与测试方案

问题

为Java的HttpFilter编写JUnit+Mockito测试,已编写doFilter()的测试代码,但代码覆盖率显示方法和行覆盖率均为0%。doFilter方法返回void,且req会被重新赋值为新实例,因此将断言放在FilterChain方法中,验证传入的请求实例是否符合预期。请问原因是什么?如何编写正确的测试?

待测试的XSSFilter代码:

public class XSSFilter implements Filter{

    public static final String XSS_FILTER_ACTIVE_PROPERTY_KEY = "XSSFilterActive";
    public static final String XSS_FILTER_ACTIVE_DEFAULT_VALUE = "false";
    private Logger logger;

    boolean isXssFilterActive= false;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        if (logger==null) {
            logger = LoggerFactory.getLogger(this.getClass().getName());
        }
        isXssFilterActive = Boolean.parseBoolean(ResourceManagerListener.getProperty(XSS_FILTER_ACTIVE_PROPERTY_KEY, XSS_FILTER_ACTIVE_DEFAULT_VALUE));
        logger.info("isXssFilterActive ={}", isXssFilterActive);
        if (isXssFilterActive) {
            logger.info("filter XSS on");
        }
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {      
        HttpServletRequest request = (HttpServletRequest) req;
        if (isXssFilterActive) {
            req = new XSSRequestWrapper(request);
        }
        chain.doFilter(req, response);
    }

    @Override
    public void destroy() {
        //empty
    }
}

现有测试代码:

public class XssFilterTest {

    @Mock
    HttpServletRequest req;

    @Mock
    ServletResponse response;
    private AutoCloseable autoCloseableMock;

    @Before
    public void setUp() throws Exception {
        autoCloseableMock = MockitoAnnotations.openMocks(this);
    }

    //test XSS filtering ON: test request instance of XSSRequestWrapper 
    @Test
    public void testXSSFilterActiveTrue() throws Exception { 

        final MockedStatic<ResourceManagerListener> resourceManagerListenerMockedStatic = mockStatic(ResourceManagerListener.class);
        when(ResourceManagerListener.getProperty("XSSFilterActive", "false")).thenReturn("true");
        XSSFilter target = new XSSFilter();
        FilterConfig config = mock(FilterConfig.class);

        target.init(config);
        
        FilterChain filterChainConsumer =
                ( request, response) -> assertThat(request.getClass()).as("check filter is on").isEqualTo(XSSRequestWrapper.class);
        HttpServletRequest request = new HttpServletRequestWrapper(req);
        target.doFilter(request,response, filterChainConsumer);
        resourceManagerListenerMockedStatic.close();
    }

    //XSS filtering off: test request instanceOf HttpServletRequestWrapper
    @Test
    public void testXSSFilterActiveFalse() throws Exception { //test quando spento

        final MockedStatic<ResourceManagerListener> resourceManagerListenerMockedStatic = mockStatic(ResourceManagerListener.class);
        when(ResourceManagerListener.getProperty("XSSFilterActive", "false")).thenReturn("false");
        XSSFilter target = new XSSFilter();
        FilterConfig config = mock(FilterConfig.class);

        target.init(config);
        FilterChain filterChainConsumer =
                ( request, response) -> assertThat(request.getClass()).as("check filter is off").isEqualTo(HttpServletRequestWrapper.class);
        HttpServletRequest request = new HttpServletRequestWrapper(req);
        target.doFilter(request,response, filterChainConsumer);
        resourceManagerListenerMockedStatic.close();
    }

    @After
    public void tearDown() throws Exception {
        autoCloseableMock.close();
    }
}
覆盖率为0%的可能原因
  1. 测试类与被测试类的路径配置问题:如果覆盖率工具(如JaCoCo)的扫描范围未包含XSSFilter所在的包,或者测试类未被识别为有效测试用例,会导致覆盖率统计为0。
  2. 测试未实际执行被测试方法:若测试类未被JUnit框架正确执行(如缺少@Test注解、未使用Mockito运行器),会导致被测试类代码未被调用,覆盖率为0。
  3. 静态Mock的资源未正确生效:如果静态Mock的时机晚于被测试类的初始化,或者ResourceManagerListener.getProperty的调用未被正确拦截,会导致init方法未按预期设置isXssFilterActive,进而doFilter的分支未被执行,但这种情况一般不会导致全0覆盖率。
  4. 覆盖率工具的缓存或配置错误:部分覆盖率工具可能存在缓存,或者配置文件中排除了被测试类,导致统计结果异常。
修正后的测试代码

以下是优化后的测试代码,解决覆盖率问题并提升测试规范性:

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.Mockito.*;
import static org.assertj.core.api.Assertions.assertThat;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

@ExtendWith(MockitoExtension.class)
public class XSSFilterTest {

    @Mock
    private HttpServletRequest mockHttpReq;
    @Mock
    private ServletResponse mockResponse;
    @Mock
    private FilterChain mockFilterChain;
    @Mock
    private FilterConfig mockFilterConfig;

    private XSSFilter xssFilter;
    private MockedStatic<ResourceManagerListener> resourceManagerStaticMock;

    @BeforeEach
    void setUp() {
        xssFilter = new XSSFilter();
        // Mock日志工厂,避免测试时输出日志并确保init方法正常执行
        try (MockedStatic<LoggerFactory> loggerFactoryMock = mockStatic(LoggerFactory.class)) {
            loggerFactoryMock.when(() -> LoggerFactory.getLogger(anyString())).thenReturn(mock(Logger.class));
            xssFilter.init(mockFilterConfig);
        }
    }

    @Test
    void doFilter_whenXssFilterActive_shouldWrapRequestWithXSSRequestWrapper() throws Exception {
        // 激活XSS过滤配置
        resourceManagerStaticMock = mockStatic(ResourceManagerListener.class);
        resourceManagerStaticMock.when(() -> ResourceManagerListener.getProperty(XSSFilter.XSS_FILTER_ACTIVE_PROPERTY_KEY, XSSFilter.XSS_FILTER_ACTIVE_DEFAULT_VALUE))
                .thenReturn("true");
        // 重新初始化过滤器,读取最新配置
        xssFilter.init(mockFilterConfig);

        ServletRequest originalRequest = new HttpServletRequestWrapper(mockHttpReq);
        xssFilter.doFilter(originalRequest, mockResponse, mockFilterChain);

        // 验证FilterChain收到的是XSSRequestWrapper实例
        verify(mockFilterChain).doFilter(argThat(req -> req instanceof XSSRequestWrapper), eq(mockResponse));
    }

    @Test
    void doFilter_whenXssFilterInactive_shouldPassOriginalRequest() throws Exception {
        // 关闭XSS过滤配置
        resourceManagerStaticMock = mockStatic(ResourceManagerListener.class);
        resourceManagerStaticMock.when(() -> ResourceManagerListener.getProperty(XSSFilter.XSS_FILTER_ACTIVE_PROPERTY_KEY, XSSFilter.XSS_FILTER_ACTIVE_DEFAULT_VALUE))
                .thenReturn("false");
        // 重新初始化过滤器,读取最新配置
        xssFilter.init(mockFilterConfig);

        ServletRequest originalRequest = new HttpServletRequestWrapper(mockHttpReq);
        xssFilter.doFilter(originalRequest, mockResponse, mockFilterChain);

        // 验证FilterChain收到的是原始请求实例
        verify(mockFilterChain).doFilter(eq(originalRequest), eq(mockResponse));
    }

    @AfterEach
    void tearDown() {
        if (resourceManagerStaticMock != null) {
            resourceManagerStaticMock.close();
        }
    }
}
关键优化点
  • 使用@ExtendWith(MockitoExtension.class)简化Mock管理,无需手动初始化和关闭Mock资源。
  • MockLoggerFactory避免测试过程中输出日志,同时确保init方法能正常执行。
  • 使用Mockito的verify和argThat替代自定义FilterChain,更符合测试规范,验证逻辑更清晰。
  • 明确验证原始请求是否被直接传递(关闭过滤时),避免依赖类型断言的潜在问题。
  • 确保静态Mock资源在测试后正确关闭,避免内存泄漏。

内容的提问来源于stack exchange,提问作者robyp7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 20:57:29