Quarkus+Gradle项目Pact契约验证出现ClassCastException问题求助
解决Quarkus+Pact契约验证中的ClassCastException问题
问题现象
基于Gradle构建的Quarkus应用,能够正常生成Pact契约,但在运行契约验证测试时抛出ClassCastException,错误信息显示au.com.dius.pact.provider.junit5.PactVerificationContext类被两个不同的类加载器加载(系统类加载器app和Quarkus专属类加载器io.quarkus.bootstrap.classloading.QuarkusClassLoader),导致类型转换失败。
错误堆栈信息:
class au.com.dius.pact.provider.junit5.PactVerificationContext cannot be cast to class au.com.dius.pact.provider.junit5.PactVerificationContext (au.com.dius.pact.provider.junit5.PactVerificationContext is in unnamed module of loader 'app'; au.com.dius.pact.provider.junit5.PactVerificationContext is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @6de54b40) java.lang.ClassCastException: class au.com.dius.pact.provider.junit5.PactVerificationContext cannot be cast to class au.com.dius.pact.provider.junit5.PactVerificationContext (au.com.dius.pact.provider.junit5.PactVerificationContext is in unnamed module of loader 'app'; au.com.dius.pact.provider.junit5.PactVerificationContext is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @6de54b40) at au.com.dius.pact.provider.junit5.PactVerificationContext.verifyInteraction(PactVerificationContext.kt:57) at com.FruitProviderTest.pactVerificationTestTemplate(FruitProviderTest.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:568) at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:973) at io.quarkus.test.junit.QuarkusTestExtension.runExtensionMethod(QuarkusTestExtension.java:903) at io.quarkus.test.junit.QuarkusTestExtension.interceptTestTemplateMethod(QuarkusTestExtension.java:863) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179) at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762) at java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:276) at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499) at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150) at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173) at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511) at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
相关代码
测试类代码
@PactFolder("build/pacts") @Provider("provider") @Testcontainers @QuarkusTest @ExtendWith(PactVerificationInvocationContextProvider.class) public class FruitProviderTest { private static final int ENVOY_PORT = 8081; @TestTemplate @ExtendWith(PactVerificationInvocationContextProvider.class) void pactVerificationTestTemplate(PactVerificationContext context) { context.verifyInteraction(); } @State(value = "Fruits Exist", action = StateChangeAction.SETUP) public void fruitsExist() { System.out.println("-----------------------Now in the 'Fruits Exist' state-------------------------"); } @State(value = "Fruits Exist", action = StateChangeAction.TEARDOWN) public void fruitsExistTearDown() { System.out.println("-----------------------Now in the 'Fruits Exist Tear Down' state-------------------------"); } }
GRPC服务实现代码
@GrpcService public class FruitServiceImpl extends MutinyFruitServiceGrpc.FruitServiceImplBase { @Override public Uni<CreateJuiceResponse> getFruit(Empty request) { return Uni.createFrom().item(CreateJuiceResponse .newBuilder().setColour("Red").setName("Apple") .build()); } }
Gradle依赖配置
dependencies { implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") implementation 'io.quarkus:quarkus-grpc' implementation 'io.quarkus:quarkus-arc' testImplementation 'io.quarkus:quarkus-junit5' implementation 'io.quarkus:quarkus-rest-client-reactive' testImplementation("au.com.dius.pact.provider:junit5:4.6.14") implementation 'com.google.api.grpc:proto-google-common-protos:2.14.3' testImplementation 'org.testcontainers:junit-jupiter:1.17.3' testImplementation 'io.rest-assured:rest-assured' testImplementation 'io.quarkus:quarkus-junit5-mockito' //testImplementation enforcedPlatform("au.com.dius.pact:pact-jvm-dependencies:4.6.14") }
解决方案
这个异常的核心原因是Pact类被Quarkus类加载器和系统类加载器重复加载,导致类型不兼容。可以通过以下步骤解决:
- 统一Pact依赖版本
取消Gradle依赖中Pact平台依赖的注释,确保所有Pact相关依赖版本一致:
testImplementation enforcedPlatform("au.com.dius.pact:pact-jvm-dependencies:4.6.14")
- 配置Quarkus类加载器优先使用父类加载器加载Pact类
在src/test/resources/application-test.properties中添加以下配置,让Quarkus的类加载器将Pact相关类交给系统类加载器处理,避免重复加载:
quarkus.classloader.parent-first-artifacts=au.com.dius.pact.provider:junit5,au.com.dius.pact:pact-jvm-dependencies
- 移除重复的注解
测试类上已经添加了@ExtendWith(PactVerificationInvocationContextProvider.class),方法上的重复注解可以移除,避免加载逻辑冲突:
修改后的测试方法:
@TestTemplate void pactVerificationTestTemplate(PactVerificationContext context) { context.verifyInteraction(); }
- 确认契约文件路径
确保build/pacts目录下存在正确生成的Pact契约文件,路径配置无误。
验证
完成上述修改后,重新运行契约验证测试,检查ClassCastException是否消失,确认契约验证流程正常执行。
内容的提问来源于stack exchange,提问作者HashTag TechTalk
相关产品推荐
相关产品推荐

