Spring Boot微服务JUnit测试:跨模块调用404及Mock方案
问题根源
你在getJWTTokenForRoleUser方法里用mockMvc请求/authenticate/login,但这个接口属于认证服务,并不在订单服务的Spring上下文内,所以必然返回404。mockMvc仅能测试当前服务(订单服务)的接口,跨服务调用需要用RestTemplate这类HTTP客户端,或者直接Mock外部依赖。
解决方案
根据测试类型(单元/集成),提供两种可行方案:
方案一:单元测试Mock外部依赖(推荐)
单元测试应聚焦订单服务自身逻辑,无需依赖真实认证服务,直接Mock关键依赖即可:
方式1:Mock JwtUtils解析逻辑
既然测试中已经用了@WithMockUser,可直接跳过真实JWT获取流程,MockJwtUtils让它返回指定用户名:
// 在测试类中添加MockBean @MockBean private JwtUtils jwtUtils; @Test @DisplayName("Place Order -- Success Scenario") @WithMockUser(username = "User", authorities = { "ROLE_USER" }) void test_When_placeOrder_DoPayment_Success() throws Exception { // 配置Mock:无论传入什么token,都返回测试用户名 when(jwtUtils.getUserNameFromJwtToken(anyString())).thenReturn("User"); OrderRequest orderRequest = getMockOrderRequest(); // 随便传入一个格式合法的字符串即可,因为JwtUtils已被Mock String jwt = "dummy-jwt-token"; MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/order/placeorder") .contentType(MediaType.APPLICATION_JSON_VALUE) .header("Authorization", "Bearer " + jwt) .content(objectMapper.writeValueAsString(orderRequest))) .andExpect(MockMvcResultMatchers.status().isOk()) .andReturn(); // 后续断言逻辑保持不变... }
方式2:Mock RestTemplate调用认证服务
如果需要模拟获取JWT的完整流程,用MockRestServiceServer拦截RestTemplate请求:
@Autowired private RestTemplate restTemplate; private MockRestServiceServer mockServer; @BeforeEach void setUp() { // 初始化Mock服务端 mockServer = MockRestServiceServer.createServer(restTemplate); } private String getJWTTokenForRoleUser() throws Exception { var loginRequest = new LoginRequest("User","User"); // 构造模拟的JWT响应 JWTResponse mockResponse = new JWTResponse(); mockResponse.setToken("valid-jwt-token"); // 配置Mock规则:匹配认证服务的login请求,返回模拟响应 mockServer.expect(requestTo("http://AUTH-SERVICE/authenticate/login")) .andExpect(method(HttpMethod.POST)) .andExpect(content().json(asJsonString(loginRequest))) .andRespond(withStatus(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON) .body(asJsonString(mockResponse))); // 发起请求会被Mock拦截,无需真实调用认证服务 JWTResponse response = restTemplate.postForObject("http://AUTH-SERVICE/authenticate/login", loginRequest, JWTResponse.class); return response.getToken(); }
注意:如果需要真实解析JWT,需保证订单服务的jwtSecret与认证服务一致,否则需继续MockJwtUtils的解析方法。
方案二:集成测试(调用真实认证服务)
若要做全链路集成测试,确保以下几点:
- 给RestTemplate添加
@LoadBalanced注解,支持服务发现:
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
- 确认认证服务已注册到Eureka,订单服务能从注册中心获取到
AUTH-SERVICE实例。 - 替换
getJWTTokenForRoleUser中的mockMvc调用为RestTemplate:
private String getJWTTokenForRoleUser() throws Exception { var loginRequest = new LoginRequest("User","User"); // 直接通过服务名调用,无需指定端口 JWTResponse response = restTemplate.postForObject("http://AUTH-SERVICE/authenticate/login", loginRequest, JWTResponse.class); return response.getToken(); }
额外优化
测试中同时使用@WithMockUser和JWT认证属于冗余操作:@WithMockUser是Spring Security的Mock认证方式,会直接模拟已登录用户;而JWT认证是通过HTTP header传递token。二者选其一即可,避免逻辑冲突。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

