You need to enable JavaScript to run this app. 如何使用Mockito模拟ServletContext以支持addListener方法完成单元测试
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Mockito模拟ServletContext以支持addListener方法完成单元测试

问题根因

你遇到的异常是Spring自带的MockServletContext的默认实现导致的:它的addListener方法默认会直接抛出UnsupportedOperationException,而且这个调用是在父类AbstractAnnotationConfigDispatcherServletInitializer的onStartup逻辑中触发的,并不是你自己写的那行监听器注册代码。

正确的单元测试写法

这里提供两种可行方案:

方案1:Spy包装MockServletContext,Stub掉addListener逻辑

如果要保留使用Spring的MockServletContext,可以用Mockito Spy包装它,拦截所有addListener调用跳过真实执行:

import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockServletContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EventListener;

public class ServletInitializerTest {
    // 直接实例化被测类,不需要@InjectMocks
    private final ServletInitializer servletInitializer = new ServletInitializer();

    @Test
    public void testOnStartup() throws ServletException {
        MockServletContext rawContext = new MockServletContext();
        ServletContext spyContext = Mockito.spy(rawContext);
        // 匹配所有监听器实例,stub掉addListener调用
        Mockito.doNothing().when(spyContext).addListener(Mockito.any(EventListener.class));

        // 测试skipCookie=N的分支
        System.setProperty("skipCookie", "N");
        servletInitializer.onStartup(spyContext);
        // 可自行添加断言验证Cookie配置是否符合预期

        // 测试完成清理系统属性,避免影响其他用例
        System.clearProperty("skipCookie");
    }
}

方案2:直接Mock ServletContext接口(更推荐单元测试使用)

直接Mock ServletContext接口完全控制所有行为,不需要依赖Spring的Mock实现的限制,还可以方便的验证分支逻辑:

import org.junit.Test;
import org.mockito.Mockito;
import javax.servlet.ServletContext;
import javax.servlet.SessionCookieConfig;
import javax.servlet.ServletException;

public class ServletInitializerTest {
    private final ServletInitializer servletInitializer = new ServletInitializer();

    @Test
    public void testOnStartupSkipCookie() throws ServletException {
        // Mock依赖对象
        ServletContext mockContext = Mockito.mock(ServletContext.class);
        SessionCookieConfig mockCookieConfig = Mockito.mock(SessionCookieConfig.class);
        Mockito.when(mockContext.getSessionCookieConfig()).thenReturn(mockCookieConfig);

        // 测试skipCookie=Y的分支
        System.setProperty("skipCookie", "Y");
        servletInitializer.onStartup(mockContext);
        // 验证Cookie配置方法没有被调用
        Mockito.verify(mockCookieConfig, Mockito.never()).setHttpOnly(true);
        Mockito.verify(mockCookieConfig, Mockito.never()).setSecure(true);

        System.clearProperty("skipCookie");
    }
}

你之前写法的错误点

  • 用@InjectMocks没有必要,该类没有需要注入的成员变量,直接实例化即可
  • Stub addListener时参数匹配错误:你用new MyServletContextListener()或者单独mock的监听器实例作为参数,和实际传入的实例不是同一个,匹配失败导致Stub不生效,需要用Mockito.any(EventListener.class)匹配所有监听器实例
  • 创建了Spy对象但没有传入onStartup方法,还是用了原始的MockServletContext实例,Stub逻辑自然不会生效

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.19 21:36:04