JUnit测试中如何用Mockito模拟RequestContextHolder.getRequest()避免空指针
问题原因
单元测试环境默认没有Web容器的请求上下文,RequestContextHolder 中没有绑定请求属性对象,调用 currentRequestAttributes() 就会返回空,后续调用 getRequest() 自然抛出空指针异常。
可行实现方案
方案1:手动构造Mock请求绑定到上下文(最通用)
Spring-test 模块自带了 MockHttpServletRequest 工具类,可以直接模拟请求对象,手动绑定到 RequestContextHolder 即可,步骤如下:
- 第一步:单元测试类引入Spring-test依赖(一般SpringBoot项目默认已集成测试启动器,无需额外引入)
- 第二步:在测试用例执行前初始化Mock请求并绑定上下文,测试执行后清理上下文,避免污染其他用例。
示例代码:
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; // 你的测试类 public class CacheLookupPollingDarwinTest { @BeforeEach void initMockRequest() { // 构造模拟请求 MockHttpServletRequest mockRequest = new MockHttpServletRequest(); // 按需设置请求的参数、Header、属性等,匹配业务逻辑需要的字段 mockRequest.addHeader("Content-Type", "application/json"); mockRequest.setParameter("bizId", "test123"); // 绑定到请求上下文 RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(mockRequest)); } @AfterEach void clearRequestContext() { // 清理上下文,避免影响其他测试用例 RequestContextHolder.resetRequestAttributes(); } // 你的测试用例 @Test void testCacheLookupPolling() { // 这里执行你的业务测试逻辑即可,不会再出现空指针 CacheLookupPolling_Darwin fragment = beanFactory.getBean(CacheLookupPolling_Darwin.class); String result = fragment.cacheLookupPolling_Darwin("testParam", httpHeaders); // 断言结果 } }
方案2:纯单元测试跳过上下文获取逻辑
如果你不需要启动Spring上下文,做纯粹的单元测试,可以直接通过反射给CacheLookupPolling_Darwin类的Request成员变量赋值模拟对象,跳过从RequestContextHolder取请求的逻辑:
示例代码:
import org.springframework.mock.web.MockHttpServletRequest; import java.lang.reflect.Field; import org.junit.jupiter.api.Test; @Test void testPureUnit() throws Exception { CacheLookupPolling_Darwin bean = new CacheLookupPolling_Darwin(); MockHttpServletRequest mockRequest = new MockHttpServletRequest(); // 按需设置mockRequest的字段 mockRequest.addHeader("your-header", "test-value"); // 反射给私有成员变量赋值 Field requestField = CacheLookupPolling_Darwin.class.getDeclaredField("Request"); requestField.setAccessible(true); requestField.set(bean, mockRequest); // 执行业务逻辑测试 }
额外优化建议
你当前把DefaultHttpServletRequest作为CacheLookupPolling_Darwin类的成员变量,如果这个类是Spring默认的单例Bean,会存在严重的线程安全问题:多个请求线程会覆盖同一个Request变量,导致并发场景下请求数据混乱,建议改成方法内部局部变量,或者把类的作用域改成@RequestScope。
内容的提问来源于stack exchange,提问作者user12505474
相关产品推荐
相关产品推荐

