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

基于Cucumber的Spring Boot GemFire客户端/服务端拓扑集成测试咨询

Spring Boot + Apache Geode Cucumber集成测试实现方案

核心问题破局:规避同一JVM的Peer/Client Cache冲突

同一JVM内无法同时运行带Locator的Peer Cache(服务端)和Client Cache(客户端),因此必须将服务端集群与客户端测试逻辑隔离在不同进程,以下两种方案均可落地:


方案1:基于Spring Data Geode手动实现进程隔离

替代失效的ForkingClientServerIntegrationTestsSupport类,通过自定义进程启动实现服务端与客户端分离:

1. 编写嵌入式服务端配置

@Configuration
@ClusterConfiguration(locatorCount = 1, serverCount = 1)
@ImportResource("classpath:cluster.xml") // 加载你的Region定义文件
public class EmbeddedGeodeServerConfig {
    // 注解自动启动Locator和Server,无需额外代码
}

2. 服务端启动入口类

单独创建主类用于启动嵌入式集群,测试时fork该进程:

public class GeodeServerTestRunner {
    public static void main(String[] args) {
        SpringApplication.run(EmbeddedGeodeServerConfig.class, args);
    }
}

3. 客户端测试配置

禁用自动配置的Peer Cache,强制创建ClientCache并连接嵌入式集群:

@SpringBootTest(properties = {
    "spring.data.gemfire.cache.type=CLIENT",
    "spring.data.gemfire.locator.host=localhost",
    "spring.data.gemfire.locator.port=10334" // Geode默认Locator端口
})
@CucumberContextConfiguration
public class GeodeClientTestConfig {
    @Autowired
    private Region<String, Object> testRegion; // 注入测试用Region
}

4. Cucumber钩子管理服务端进程

在测试前后启动/停止服务端进程:

public class GeodeTestHooks {
    private static Process serverProcess;

    @BeforeAll
    public static void startGeodeServer() throws IOException {
        String javaHome = System.getProperty("java.home");
        String classpath = System.getProperty("java.class.path");
        
        ProcessBuilder processBuilder = new ProcessBuilder(
            javaHome + "/bin/java",
            "-cp", classpath,
            "com.yourpackage.GeodeServerTestRunner"
        );
        serverProcess = processBuilder.start();
        
        // 等待集群启动完成,可根据实际情况调整时长
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    @AfterAll
    public static void stopGeodeServer() {
        if (serverProcess != null) {
            serverProcess.destroy();
        }
    }
}

方案2:用Testcontainers运行容器化Geode集群(更稳定)

无需手动管理进程,直接用容器化集群模拟生产环境:

1. 添加Testcontainers依赖

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>apache-geode</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>junit-jupiter</artifactId>
    <scope>test</scope>
</dependency>

2. 客户端测试配置

@SpringBootTest(properties = {
    "spring.data.gemfire.cache.type=CLIENT"
})
@CucumberContextConfiguration
public class GeodeClientTestConfig {
    @Container
    private static final ApacheGeodeContainer geodeContainer = new ApacheGeodeContainer("apachegeode/geode:1.15.0")
            .withClusterXmlMounted(Paths.get("src/test/resources/cluster.xml")); // 挂载Region定义文件

    @DynamicPropertySource
    static void registerGeodeProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.data.gemfire.locator.host", geodeContainer::getLocatorHost);
        registry.add("spring.data.gemfire.locator.port", geodeContainer::getLocatorPort);
    }

    @Autowired
    private Region<String, Object> testRegion;
}

缓存CRUD测试用例实现

Feature文件(src/test/resources/features/cache_operations.feature)

Feature: Geode Cache CRUD Operations
  Scenario: 创建并查询缓存条目
    Given 缓存中存在键为"testKey"、值为"testValue"的条目
    When 根据键"testKey"查询条目
    Then 查询结果应为"testValue"

  Scenario: 删除缓存条目
    Given 缓存中存在键为"deleteKey"、值为"deleteValue"的条目
    When 根据键"deleteKey"删除条目
    Then 缓存中不存在键为"deleteKey"的条目

Step定义类

public class CacheOperationsSteps {
    @Autowired
    private Region<String, Object> testRegion;

    @Given("缓存中存在键为{string}、值为{string}的条目")
    public void createCacheEntry(String key, String value) {
        testRegion.put(key, value);
    }

    @When("根据键{string}查询条目")
    public void retrieveCacheEntry(String key) {
        ScenarioContext.getCurrentContext().put("retrievedValue", testRegion.get(key));
    }

    @Then("查询结果应为{string}")
    public void verifyRetrievedValue(String expectedValue) {
        String actualValue = (String) ScenarioContext.getCurrentContext().get("retrievedValue");
        assertEquals(expectedValue, actualValue);
    }

    @When("根据键{string}删除条目")
    public void deleteCacheEntry(String key) {
        testRegion.remove(key);
    }

    @Then("缓存中不存在键为{string}的条目")
    public void verifyEntryDeleted(String key) {
        assertNull(testRegion.get(key));
    }
}

关键注意事项

  • cluster.xml验证:确保文件放在src/test/resources下,服务端启动后可通过日志检查Region是否成功创建。
  • 端口冲突处理:手动fork进程时,可配置服务端使用随机端口,避免本地端口占用:
    @Bean
    public LocatorFactoryBean locator() {
        LocatorFactoryBean locator = new LocatorFactoryBean();
        locator.setPort(0); // 随机分配端口
        return locator;
    }
    
    之后通过服务端日志或端口检测工具获取实际端口,传递给客户端配置。
  • 启动等待时长:手动启动服务端时,需根据机器性能调整等待时间,避免客户端连接失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 08:40:41