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

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类加载器和系统类加载器重复加载,导致类型不兼容。可以通过以下步骤解决:

  1. 统一Pact依赖版本
    取消Gradle依赖中Pact平台依赖的注释,确保所有Pact相关依赖版本一致:
testImplementation enforcedPlatform("au.com.dius.pact:pact-jvm-dependencies:4.6.14")
  1. 配置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
  1. 移除重复的注解
    测试类上已经添加了@ExtendWith(PactVerificationInvocationContextProvider.class),方法上的重复注解可以移除,避免加载逻辑冲突:
    修改后的测试方法:
@TestTemplate
void pactVerificationTestTemplate(PactVerificationContext context) {
    context.verifyInteraction();
}
  1. 确认契约文件路径
    确保build/pacts目录下存在正确生成的Pact契约文件,路径配置无误。

验证

完成上述修改后,重新运行契约验证测试,检查ClassCastException是否消失,确认契约验证流程正常执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 00:24:52