You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的解析方法。

方案二:集成测试(调用真实认证服务)

若要做全链路集成测试,确保以下几点:

  1. 给RestTemplate添加@LoadBalanced注解,支持服务发现:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
    return new RestTemplate();
}
  1. 确认认证服务已注册到Eureka,订单服务能从注册中心获取到AUTH-SERVICE实例。
  2. 替换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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 10:35:23