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

如何测试调用第三方REST端点且需Mock该端点的业务服务?

实现方案

针对这个场景有两种主流的测试方案,可根据你的测试粒度选择:

方案一:轻量集成测试(推荐,无需启动Web容器)

使用Spring自带的MockRestServiceServer直接模拟RestTemplate的返回,不需要额外编写Mock Controller,也不用启动内置Tomcat,执行速度快,适合验证服务层的请求逻辑正确性。

前置修改

首先调整业务代码的硬编码URI,改为配置注入,方便测试时动态修改:

@Service
public class SomeServiceImpl {
    @Autowired
    private RestTemplate restTemplate;

    // 从配置文件读取URI,默认值保留原来的线上地址
    @Value("${client.system.uri:http://localhost:8080/foo}")
    private String clientSystemUri;

    public void sendToClient(Entity entity){
        var clientSystem = new ClientSystemRequest();
        clientSystem.setImportField(entity);
        log.info("Sending request to client system");
        restTemplate.postForObject(clientSystemUri, clientSystem, ClientSystemResponse.class);
    }
}

测试用例编写

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {SomeServiceImpl.class, RestTemplateAutoConfiguration.class})
public class FooExampleTest {
    @Autowired
    private SomeServiceImpl exampleService;
    @Autowired
    private RestTemplate restTemplate;

    private MockRestServiceServer mockServer;

    @BeforeEach
    void setup() {
        // 初始化Mock Server绑定当前的RestTemplate
        mockServer = MockRestServiceServer.createServer(restTemplate);
    }

    @Test
    void example_TestCase() {
        // 构造测试参数
        SomeEntity entity = new SomeEntity();
        entity.setFoo("Foo!");

        // 定义预期的请求和返回
        mockServer.expect(ExpectedCount.once(), 
                RequestMatchers.postTo("http://localhost:8080/foo"))
                .andExpect(RequestMatchers.content().json(/* 你预期的请求体JSON字符串 */))
                .andRespond(ResponseCreators.withSuccess("{\"code\":200}", MediaType.APPLICATION_JSON));

        // 调用业务方法
        exampleService.sendToClient(entity);

        // 验证所有预期请求都已触发
        mockServer.verify();
    }
}

你还可以自定义返回的HTTP状态码、响应体,用来测试你的服务对第三方异常返回的处理逻辑。

方案二:全量集成测试(启动Web容器,使用自定义Mock Controller)

如果需要启动完整的Spring Boot上下文,跑端到端的全链路流程,可以使用你已经编写的Mock Controller,按以下步骤调整即可:

前置修改

  1. 修正Mock Controller的类名,Java类名不能以数字开头,改为Mock3rdPartyClientSystemController
  2. 确保测试时Spring能扫描到这个Mock Controller,可将其放在测试代码的根包下,或者在测试类上添加@ComponentScan指定扫描路径
  3. 业务代码还是保持上面的URI配置注入的方式

测试用例编写

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FooExampleTest {
    @Autowired
    private SomeServiceImpl exampleService;
    // 注入Mock Controller,用来验证收到的请求
    @Autowired
    private Mock3rdPartyClientSystemController mockController;
    // 拿到当前测试服务启动的随机端口
    @LocalServerPort
    private int port;

    // 动态修改业务代码里的第三方URI,指向当前启动的测试服务
    @TestConfiguration
    static class TestConfig {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate();
        }
        
        @Value("${local.server.port}")
        private int localPort;

        @Bean
        public String clientSystemUri() {
            return "http://localhost:" + localPort + "/foo";
        }
    }

    @Test
    void example_TestCase() {
        SomeEntity entity = new SomeEntity();
        entity.setFoo("Foo!");

        exampleService.sendToClient(entity);

        // 验证Mock Controller收到的请求是否符合预期
        ClientSystemRequest receivedRequest = mockController.getLatestRequest(); // 你需要在Mock Controller里加个存储请求的方法
        Assertions.assertEquals("Foo!", receivedRequest.getImportField().getFoo());
    }
}

对应的Mock Controller调整:

@RestController
@Slf4j
public class Mock3rdPartyClientSystemController {
    // 存储最近收到的请求,用于测试断言
    private ClientSystemRequest latestRequest;

    @PostMapping("/foo")
    public ResponseEntity<?> foo(@RequestBody @Valid ClientSystemRequest clientSystemRequest){
        log.info("Received, with data: {}", clientSystemRequest);
        this.latestRequest = clientSystemRequest;
        // 可根据测试需求自定义返回异常、自定义响应体
        return new ResponseEntity<>(HttpStatus.OK);
    }

    public ClientSystemRequest getLatestRequest() {
        return latestRequest;
    }
}

通用注意事项

  • 如果你的服务有重试、降级逻辑,可在Mock侧设置多次返回不同结果,验证容错逻辑是否符合预期
  • 所有请求参数、响应的序列化/反序列化逻辑会被真实执行,不会跳过转换逻辑,测试覆盖度更高

内容的提问来源于stack exchange,提问作者tea_baggins

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 10:18:01