微服务架构下结合真实数据库与真实HTTP调用的测试方案及跨服务调用连接拒绝问题排查
微服务架构下结合真实数据库与真实HTTP调用的测试方案及跨服务调用连接拒绝问题排查
看起来你在微服务集成测试里踩了几个典型的小坑——跨服务调用时的连接拒绝,还有对Mock工具的误用,我之前做微服务测试的时候也碰到过几乎一样的问题,来给你一步步理清楚怎么解决:
先拆解你当前代码里的核心问题
- Mock对象导致真实调用失效:你用了
@Mock private ApiClient apiClient,这个Mock对象是个“假的”客户端,它不会真的发送HTTP请求到用户服务,所以当authService.createInvitationForUserToJoin执行时,本质上是在调用一个空实现;如果你的ApiClient配置里硬编码了localhost:8080,又没有真实服务在这个端口启动,自然就会报连接拒绝。 - MockMvc的误用:你在测试里用
mockMvc.perform(get("/users/{id}", id)),但MockMvc是绑定到当前认证服务的Spring MVC上下文的,/users/{id}是用户服务的接口,认证服务里根本没有这个控制器,所以这个测试步骤本身就会返回404,完全起不到验证用户服务接口的作用。 - 未启动真实HTTP服务器:默认的
@SpringBootTest不会启动真实的内嵌服务器(除非指定webEnvironment),所以即使你有真实的ApiClient,也没有可访问的HTTP端口来处理请求。
解决方案:构建真实数据库+真实跨服务调用的集成测试
你的Testcontainers配置其实已经搞定了真实数据库的部分,现在只需要补充“启动真实服务”和“正确配置跨服务调用”的环节:
步骤1:让测试启动真实的内嵌服务器
修改@SpringBootTest注解,指定随机端口启动真实的Web服务,避免本地端口冲突:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
步骤2:用Testcontainers启动用户服务实例
如果你的用户服务已经打包成Docker镜像,直接用GenericContainer启动它,并且让它连接到同一个Testcontainers管理的PostgreSQL:
@Container static GenericContainer<?> userServiceContainer = new GenericContainer<>("your-user-service-image:latest") .withExposedPorts(8080) // 配置用户服务连接测试数据库 .withEnv("SPRING_DATASOURCE_URL", "jdbc:postgresql://" + postgreSQLContainer.getHost() + ":" + postgreSQLContainer.getMappedPort(5432) + "/testdb") .withEnv("SPRING_DATASOURCE_USERNAME", "postgres") .withEnv("SPRING_DATASOURCE_PASSWORD", "testpass");
如果是多模块项目不想打包Docker,也可以在测试类中直接引入用户服务的Spring上下文(启动速度会慢一些,但适合本地开发):
@SpringBootTest(classes = {AuthServiceApplication.class, UserServiceApplication.class})
步骤3:配置真实的ApiClient,指向用户服务地址
去掉@Mock private ApiClient apiClient,改用@Autowired注入真实的客户端实例,然后在测试初始化时配置用户服务的真实地址:
@Autowired private ApiClient apiClient; @BeforeEach void setUp() { // 获取用户服务容器的真实地址(IP+端口) String userServiceBaseUrl = "http://" + userServiceContainer.getHost() + ":" + userServiceContainer.getMappedPort(8080); apiClient.setBasePath(userServiceBaseUrl); }
步骤4:调整测试逻辑,用真实HTTP客户端验证接口
放弃用MockMvc调用其他服务的接口,改用TestRestTemplate或WebClient调用用户服务的真实HTTP接口:
@Autowired private TestRestTemplate testRestTemplate; @Test void testCreateInvitation() throws Exception { // 1. 调用用户服务的创建用户接口 AdminUser dummyAdmin = TestUtils.dummyAdminUser(); CreateUserRequestDTO createUserRequest = DTOConverter.toCreateUserRequestDTO(dummyAdmin); User createdUser = testRestTemplate.postForObject("/users", createUserRequest, User.class); assertNotNull(createdUser); // 2. 验证用户服务的查询接口可正常访问 User fetchedUser = testRestTemplate.getForObject("/users/{id}", User.class, createdUser.getId()); assertEquals(createdUser.getId(), fetchedUser.getId()); // 3. 调用认证服务的邀请创建方法,触发跨服务调用 authService.createInvitationForUserToJoin(createdUser.getId()); // 这里可以添加断言,比如检查数据库中是否生成了对应的邀请记录 // Invitation invitation = invitationService.findByUserId(createdUser.getId()); // assertNotNull(invitation); }
额外的连接拒绝排查技巧
如果还是报连接拒绝,你可以从这几个方向查:
- 检查用户服务容器是否真的启动成功:在测试里打印
userServiceContainer.isRunning(),或者查看Testcontainers的日志输出。 - 验证容器网络连通性:Testcontainers默认会给所有容器分配同一个网络,所以用容器IP调用没问题,不要硬编码
localhost,因为容器里的localhost和本地主机的localhost不是同一个环境。 - 检查用户服务的配置:确保用户服务的端口是8080,或者在
withExposedPorts里指定正确的暴露端口。 - 关闭本地防火墙:有时候本地防火墙会阻止容器之间的连接请求。
内容来源于stack exchange
相关产品推荐
相关产品推荐

