Spring Cloud Contract测试因拦截器preHandle设置请求属性失败
问题1:Spring Cloud Contract测试中拦截器无法获取客户端ID的解决方案
核心原因
你的拦截器在preHandle中调用的认证服务在SCC测试环境下未被存根,导致返回null,进而无法设置到HttpServletRequest中。直接存根HttpServletRequest.getAttribute()无效,因为拦截器是先设置属性,控制器才会读取,问题根源在认证服务的调用而非request的读取逻辑。
正确解决方案:模拟认证服务
不要硬编码ID或存根业务服务,而是直接模拟拦截器依赖的认证服务,让它返回预设的客户端ID,这样拦截器就能正常完成逻辑,控制器也能拿到正确的参数。
示例代码
假设你的认证服务接口是AuthService,在测试类中使用@MockBean模拟它:
import org.springframework.boot.test.mock.mockito.MockBean; import static org.mockito.Mockito.when; @SpringBootTest @AutoConfigureMockMvc public class YourControllerContractTest { @MockBean private AuthService authService; @Autowired private MockMvc mockMvc; @Test void testContractEndpoint() throws Exception { // 模拟认证服务返回预设的客户端ID when(authService.getClientId(any(HttpServletRequest.class))).thenReturn("test-client-123"); // 执行SCC测试请求 mockMvc.perform(get("/your-endpoint")) .andExpect(status().isOk()); } }
额外注意点
- 确保拦截器在测试上下文被正确加载:如果使用
@WebMvcTest,需要手动注册拦截器(通过@TestConfiguration),因为@WebMvcTest默认只加载指定控制器和相关组件,不会自动加载所有拦截器。 - 避免排除拦截器的测试配置:检查测试类的
@SpringBootTest或@WebMvcTest是否有excludeFilters排除了拦截器所在的包。
问题2:Spring Cloud Contract(SCC)测试的执行逻辑
SCC测试分为生产者侧和消费者侧,两者执行逻辑差异明显:
生产者侧测试
- 目的:验证你的服务实际行为是否符合契约定义。
- 执行逻辑:启动完整的Spring应用上下文,加载所有真实组件(包括拦截器、业务服务、仓库等)。
- 数据库相关:如果你的仓库依赖数据库,默认会使用配置的测试数据库(比如H2内存库,需在
application-test.yml中配置),会执行真实的CRUD操作;如果未配置数据库,会因数据源不可用直接失败。 - 模拟规则:不会自动模拟内部组件(如仓库),除非你手动用
@MockBean或Mockito进行模拟。
消费者侧测试
- 目的:验证你的服务(作为消费者)是否能正确与生产者服务(通过契约存根)交互。
- 执行逻辑:
- 启动消费者的Spring上下文,加载自身的组件(拦截器、业务服务、仓库等)。
- 通过Stub Runner加载生产者的契约存根,将对生产者服务的调用转发到存根(而非真实服务)。
- 内部组件处理:消费者自身的仓库、业务服务等默认会执行真实逻辑,不会自动生成模拟返回值。如果这些组件依赖外部资源(如数据库),必须配置测试环境的依赖(如H2),否则会因资源不可用失败。只有当你手动模拟这些组件时,才会返回预设的模拟值。
关于“参数正确就生成模拟返回值”的误解
SCC不会自动为你的内部组件生成模拟返回值,这个逻辑是Mockito等测试框架的功能。你看到的“仓库方法返回值”,要么是测试数据库返回的真实数据,要么是你之前通过@MockBean配置的模拟值,和SCC本身无关。
内容的提问来源于stack exchange,提问作者fatjojo
相关产品推荐
相关产品推荐

