如何使用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
相关产品推荐
相关产品推荐

