Quarkus集成测试未加载测试属性,误用环境变量排查
问题
我有一个消费Kafka主题的Quarkus应用,用TestContainers编写了Kafka消费者集成测试。但运行测试时发现,明明已经定义了测试属性,系统还是会读取.zshrc文件里KAFKA_BOOTSTRAP_SERVERS环境变量指向的真实Broker地址,而非测试配置的地址。删掉.zshrc里的这个环境变量后测试就能正常运行,但保留的话就会尝试连接开发环境服务器,请问哪里遗漏了?
应用配置
kafka: bootstrap: servers: ${KAFKA_BOOTSTRAP_SERVERS:localhost:9092} security: protocol: SASL_SSL sasl: mechanism: SCRAM-SHA-512 jaas: config: org.apache.kafka.common.security.scram.ScramLoginModule required username="${KAFKA_CONSUMER_USERNAME}" password="${KAFKA_CONSUMER_PASSWORD}"; "%test": kafka: bootstrap: servers: kafka:9092 security: protocol: PLAINTEXT sasl: mechanism: PLAIN
KafkaResource类
public class KafkaResource implements QuarkusTestResourceLifecycleManager { private static KafkaContainer kafkaContainer; private static String bootstrapServers; @Override public Map<String, String> start() { kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")) .withKraft(); kafkaContainer.start(); bootstrapServers = kafkaContainer.getBootstrapServers(); return Map.of("kafka.bootstrap.servers", kafkaContainer.getBootstrapServers()); return Collections.emptyMap(); } @Override public void stop() { kafkaContainer.stop(); } public static String getBootstrapServers() { return bootstrapServers; } }
消费者测试代码
@QuarkusTest @QuarkusTestResource(KafkaResource.class) class ConsumerTest { @InjectSpy EventConsumer eventConsumer; @Test void testConsumer() throws InvalidProtocolBufferException, InterruptedException { String eventId = "sport1234"; Event event = Event.newBuilder().setEventId(eventId) .build(); produceTestMessage(event); Mockito.verify(eventConsumer, timeout(5000)).consumeState(event.toByteArray()); System.out.println("Number of invocations: " + Mockito.mockingDetails(eventConsumer).getInvocations().size()); } private void produceTestMessage(Event event) { // Create a Kafka producer configuration String kafkaBootstrapServers = KafkaResource.getBootstrapServers(); Properties producerProps = new Properties(); producerProps.put("bootstrap.servers", kafkaBootstrapServers); producerProps.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer"); producerProps.put("value.serializer", "com.org.events.EventProtoKafkaSerializer"); try (Producer<String, Event> producer = new KafkaProducer<>(producerProps)) { ProducerRecord<String, Event> record = new ProducerRecord<>("test_topic", event.getEventId(), event); producer.send(record).get(); // Wait for the message to be sent } catch (Exception e) { throw new RuntimeException("Error producing test message", e); } } }
依赖配置
implementation 'io.quarkus:quarkus-smallrye-reactive-messaging-kafka' testImplementation 'io.quarkus:quarkus-junit5' testImplementation 'io.quarkus:quarkus-junit5-mockito' testImplementation 'io.rest-assured:rest-assured' testImplementation 'io.quarkus:quarkus-jacoco' testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.testcontainers:junit-jupiter:1.19.3' testImplementation 'org.testcontainers:mysql:1.19.3' testImplementation 'org.testcontainers:kafka:1.19.3'
解决方案
问题核心是系统环境变量的优先级高于Quarkus测试配置,再加上测试配置的写法和TestResource的属性覆盖逻辑没处理到位,导致环境变量直接覆盖了测试配置。
具体原因
- Quarkus属性优先级规则:系统环境变量 > 配置文件中的测试环境配置。本地
KAFKA_BOOTSTRAP_SERVERS环境变量存在时,会直接跳过application.yaml里%test块的配置。 - 你在
%test块中写的kafka.bootstrap.servers: kafka:9092无法生效,因为环境变量的优先级更高;同时TestResource返回的属性默认优先级也低于环境变量。
解决步骤
1. 修改KafkaResource,强制覆盖环境变量
在TestResource启动时,直接设置系统属性(优先级高于环境变量)并清除对应的环境变量,确保测试用TestContainer的地址:
@Override public Map<String, String> start() { kafkaContainer = new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:latest")) .withKraft(); kafkaContainer.start(); bootstrapServers = kafkaContainer.getBootstrapServers(); // 设置系统属性,优先级高于环境变量 System.setProperty("kafka.bootstrap.servers", bootstrapServers); // 清除环境变量避免干扰 System.getenv().remove("KAFKA_BOOTSTRAP_SERVERS"); return Map.of("kafka.bootstrap.servers", bootstrapServers); }
2. 调整application.yaml的测试配置写法
修改测试环境的配置,让它优先使用TestResource注入的属性,而非读取系统环境变量:
"%test": kafka: bootstrap: servers: "${kafka.bootstrap.servers:kafka:9092}" security: protocol: PLAINTEXT sasl: mechanism: PLAIN
3. 可选:用@TestPropertySource强制锁死配置
在测试类上添加注解,进一步确保测试时使用指定属性:
@QuarkusTest @QuarkusTestResource(KafkaResource.class) @TestPropertySource(properties = {"kafka.bootstrap.servers=${kafka.bootstrap.servers}"}) class ConsumerTest { // ... 测试代码 }
验证方法
可以在测试中注入配置属性,打印实际使用的地址确认是否生效:
@Inject @ConfigProperty(name = "kafka.bootstrap.servers") String actualBootstrapServers; @Test void verifyBootstrapAddress() { System.out.println("实际使用的Kafka地址: " + actualBootstrapServers); // 正常应该输出TestContainer的地址,比如localhost:xxxx }
内容的提问来源于stack exchange,提问作者APK
相关产品推荐
相关产品推荐

