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

Pact-JVM中为多个Provider设置Mock端口的技术问题

解决Pact多Provider场景下的端口配置问题

方案一:动态适配MockServer随机端口(推荐)

利用Spring Boot的@DynamicPropertySource动态覆盖应用配置,将MockServer的随机端口注入到应用配置中,无需修改静态的application.yml,彻底避免端口冲突问题。

实现步骤

  1. 在测试类中通过@PactTestFor关联对应Provider,并注入每个Provider的MockServer实例。
  2. 使用@DynamicPropertySource静态方法,将MockServer的实际端口替换掉配置文件中的固定端口值。

代码示例

import au.com.dius.pact.consumer.MockServer;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt;
import au.com.dius.pact.consumer.junit5.PactTestFor;
import au.com.dius.pact.core.model.RequestResponsePact;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.DynamicPropertyRegistry;

@ExtendWith(PactConsumerTestExt.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ContractConsumerTest {

    @Pact(provider = "provider1", consumer = "my-spring-boot-consumer")
    public RequestResponsePact createProvider1Pact(PactDslWithProvider builder) {
        return builder
                .given("provider1 has data")
                .uponReceiving("request to provider1")
                .path("/api/provider1/data")
                .method("GET")
                .willRespondWith()
                .status(200)
                .body("{\"id\": 1, \"name\": \"test1\"}")
                .toPact();
    }

    @Pact(provider = "provider2", consumer = "my-spring-boot-consumer")
    public RequestResponsePact createProvider2Pact(PactDslWithProvider builder) {
        return builder
                .given("provider2 has data")
                .uponReceiving("request to provider2")
                .path("/api/provider2/data")
                .method("GET")
                .willRespondWith()
                .status(200)
                .body("{\"id\": 2, \"name\": \"test2\"}")
                .toPact();
    }

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry,
                                    @PactTestFor(providerName = "provider1") MockServer provider1MockServer,
                                    @PactTestFor(providerName = "provider2") MockServer provider2MockServer) {
        registry.add("provider1.base-url", () -> "http://localhost:" + provider1MockServer.getPort());
        registry.add("provider2.base-url", () -> "http://localhost:" + provider2MockServer.getPort());
    }

    @Test
    @PactTestFor(pactMethod = "createProvider1Pact")
    @PactTestFor(pactMethod = "createProvider2Pact")
    public void testConsumerCallsBothProviders() {
        // 调用Spring Boot服务逻辑,会自动使用动态注入的MockServer端口
        // 示例:yourService.callProvider1(); yourService.callProvider2();
    }
}

说明

  • @DynamicPropertySource会在测试启动时,自动将两个MockServer的随机端口替换掉application.yml中对应的固定端口配置。
  • 该方案无需修改静态配置,适配本地开发和CI环境,完全规避端口冲突风险。

方案二:为每个MockServer指定独立固定端口

如果需要和现有静态配置对齐,可为每个Provider的MockServer指定不同的固定端口,确保端口不重复即可避免占用异常。

代码示例

import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt;
import au.com.dius.pact.consumer.junit5.PactTestFor;
import au.com.dius.pact.core.model.RequestResponsePact;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;

@ExtendWith(PactConsumerTestExt.class)
@SpringBootTest
public class ContractConsumerTest {

    @Pact(provider = "provider1", consumer = "my-spring-boot-consumer")
    public RequestResponsePact createProvider1Pact(PactDslWithProvider builder) {
        // 契约定义同方案一
    }

    @Pact(provider = "provider2", consumer = "my-spring-boot-consumer")
    public RequestResponsePact createProvider2Pact(PactDslWithProvider builder) {
        // 契约定义同方案一
    }

    @Test
    @PactTestFor(providerName = "provider1", port = "1000") // 指定provider1的MockServer端口为1000
    @PactTestFor(providerName = "provider2", port = "2000") // 指定provider2的MockServer端口为2000
    public void testConsumerCallsBothProviders() {
        // 测试逻辑,应用配置的localhost:1000和localhost:2000会直接指向对应MockServer
    }
}

注意事项

  • 需确保测试环境中指定的端口未被其他进程占用,否则会抛出端口绑定异常。
  • 若使用并行测试,需避免多个测试用例同时占用这些端口,可在测试前后清理端口资源,或在CI环境中使用端口隔离机制。

内容的提问来源于stack exchange,提问作者Kevin de Boer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 18:21:32