Spring Boot微服务JUnit测试:跨服务获取并使用JWT Token
Spring Boot微服务Controller测试中JWT Token获取与配置读取问题
问题描述
在Spring Boot微服务项目中编写支付、商品、订单服务的JUnit Controller测试时,遇到以下问题:
项目引入Auth服务处理用户创建和JWT登录,API网关启用JWTFilter后,所有测试请求必须携带Bearer Token。尝试自行生成Token时,无法读取application-test.yaml中的jwt.secret配置,触发报错。
报错信息
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jwt.secret' in value "${jwt.secret}"
相关代码
测试方法示例
@Test @DisplayName("Place Order -- Success Scenario") void test_When_placeOrder_DoPayment_Success() throws Exception { OrderRequest orderRequest = getMockOrderRequest(); String jwt = getJWTTokenForRoleUser(); 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(); String orderId = mvcResult.getResponse().getContentAsString(); Optional<Order> order = orderRepository.findById(Long.valueOf(orderId)); assertTrue(order.isPresent()); Order o = order.get(); assertEquals(Long.parseLong(orderId), o.getId()); assertEquals("PLACED", o.getOrderStatus()); assertEquals(orderRequest.getTotalAmount(), o.getAmount()); assertEquals(orderRequest.getQuantity(), o.getQuantity()); }
Token生成相关代码
private String getJWTTokenForRoleUser(){ var loginRequest = new LoginRequest("User1","user1"); String jwt = jwtUtils.generateJwtToken(loginRequest.getUsername()); return jwt; } @Data @AllArgsConstructor @NoArgsConstructor public class LoginRequest { private String username; private String password; }
JwtUtils类实现
@Component public class JwtUtils { private static final Logger LOGGER = LoggerFactory.getLogger(JwtUtils.class); @Value("${jwt.secret}") private String jwtSecret; @Value("${jwt.expireMs}") private int jwtExpirationMs; public String generateJwtToken(String username) { return generateTokenFromUsername(username); } public String generateTokenFromUsername(String username) { return Jwts.builder().setSubject(username).setIssuedAt(new Date()) .setExpiration(new Date((new Date()).getTime() + jwtExpirationMs)) .signWith(SignatureAlgorithm.HS512, jwtSecret).compact(); } }
项目运行步骤
- 运行Service Registry(Eureka Server)
- 运行Config Server
- 通过Docker命令启动Zipkin和Redis:
docker run -d -p 9411:9411 openzipkin/zipkin docker run -d --name redis -p 6379:6379 redis
- 运行API网关
- 运行Auth服务、商品服务、订单服务和支付服务
解决方案建议
1. 确保测试环境正确加载配置文件
- 在测试类上添加注解指定测试配置文件:
@SpringBootTest @TestPropertySource(locations = "classpath:application-test.yaml") public class OrderControllerTest { // 测试代码 } - 若依赖Config Server,可在测试配置中直接写入
jwt.secret和jwt.expireMs,或关闭Config Server依赖:# application-test.yaml spring.cloud.config.enabled: false jwt.secret: your-test-secret-key jwt.expireMs: 3600000
2. 调用Auth服务真实接口获取Token(集成测试)
- 注入
RestTemplate,调用Auth服务的登录接口获取真实Token:@Autowired private RestTemplate restTemplate; @Autowired private ObjectMapper objectMapper; private String getJWTTokenFromAuthService() throws JsonProcessingException { LoginRequest loginRequest = new LoginRequest("User1", "user1"); ResponseEntity<String> response = restTemplate.postForEntity( "http://localhost:8080/auth/login", // Auth服务的实际地址 loginRequest, String.class ); JsonNode jsonNode = objectMapper.readTree(response.getBody()); return jsonNode.get("token").asText(); } - 此方式需保证Auth服务在测试时处于运行状态,属于集成测试场景。
3. Mock JwtUtils或跳过JWT验证(单元测试)
- 若为单元测试,Mock
JwtUtils返回固定Token:@MockBean private JwtUtils jwtUtils; @BeforeEach void setUp() { when(jwtUtils.generateJwtToken(anyString())).thenReturn("mock-valid-jwt-token"); } - 或直接关闭过滤器,跳过JWT验证:
@AutoConfigureMockMvc(addFilters = false) public class OrderControllerTest { // 测试代码 }
4. 检查配置文件位置与优先级
- 确保
application-test.yaml放置在src/test/resources目录下,测试时能被正确加载。 - 若存在多环境配置,确认测试环境的配置优先级高于其他环境。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

