单元测试调用Response.status()抛出AbstractMethodError求助
单元测试调用Response.status()抛出AbstractMethodError异常排查解决
问题场景
新编写单元测试时,调用以下业务代码抛出异常,但直接运行非测试代码时无此问题:
Response.status(responseStatus).entity(response).build();
说明:Response为javax.ws.rs.core.Response,responseStatus是javax.ws.rs.core.Response.Status类型,response为String类型。
异常栈信息
java.lang.AbstractMethodError: Receiver class org.apache.wink.common.internal.ResponseImpl$ResponseBuilderImpl does not define or inherit an implementation of the resolved method 'abstract javax.ws.rs.core.Response$ResponseBuilder status(int, java.lang.String)' of abstract class javax.ws.rs.core.Response$ResponseBuilder. at javax.ws.rs.core.Response$ResponseBuilder.status(Response.java:921) at javax.ws.rs.core.Response.status(Response.java:592) at javax.ws.rs.core.Response.status(Response.java:603) at c.i.c.riker.services.v3.resource.VendorWorkOrderV3Resource.rtsWorkOrderCreateMultipart(VendorWorkOrderV3Resource.java:947) at c.i.c.riker.services.v3.resource.VendorWorkOrderV3ResourceUnitTest.testRtsWorkOrderCreateMultipart(VendorWorkOrderV3UnitTest.java:134) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ... at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:137) at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:100) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
技术栈
使用Maven、Mockito、JUnit 5、Open Liberty服务器
项目pom.xml内容
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <licenses> <license> <name>The Apache Software License, Version 2.0</name> <url>https://raw.github.com/WASdev/ci.maven.tools/master/LICENSE</url> <distribution>repo</distribution> </license> </licenses> <parent> <groupId>com.ibm.cio.riker</groupId> <artifactId>riker</artifactId> <version>1.0.0</version> <relativePath>../</relativePath> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>riker-services</artifactId> <packaging>war</packaging> <name>rikerServices</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>net.wasdev.maven.tools.targets</groupId> <artifactId>liberty-target</artifactId> <version>RELEASE</version> <type>pom</type> <scope>provided</scope> </dependency> <dependency> <groupId>com.ibm.cio.riker</groupId> <artifactId>riker-timer</artifactId> <version>1.0.0</version> </dependency> <dependency> <groupId>org.eclipse.microprofile.openapi</groupId> <artifactId>microprofile-openapi-api</artifactId> <version>2.0</version> </dependency> <!-- newly added dependency --> <dependency> <groupId>com.ibm.cio.riker</groupId> <artifactId>riker-common</artifactId> <version>1.0.0</version> <scope>test</scope> <type>test-jar</type> </dependency> </dependencies> <build> <finalName>riker-services</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.3.2</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.3.1</version> <executions> <execution> <id>copy-package-config</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>../src/main/liberty/config</outputDirectory> <resources> <resource> <directory>src/main/liberty/config</directory> </resource> </resources> </configuration> </execution> <execution> <id>copy-package-war</id> <phase>package</phase> <goals> <goal>copy-resources</goal> </goals> <configuration> <outputDirectory>../target</outputDirectory> <resources> <resource> <directory>./target</directory> <includes> <include>riker-services.war</include> </includes> </resource> </resources> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>3.0.0</version> <configuration> <systemPropertyVariables> <http.port>${liberty.var.default.http.port}</http.port> <context.root>${liberty.var.default.contextroot}</context.root> </systemPropertyVariables> </configuration> </plugin> <plugin> <groupId>org.jvnet.jax-ws-commons</groupId> <artifactId>jaxws-maven-plugin</artifactId> <version>2.3</version> <executions> </executions> <configuration> <target>2.2</target> <sourceDestDir>${basedir}/src/main/java</sourceDestDir> <wsdlDirectory>${basedir}/src/main/resources/wsdl</wsdlDirectory> <wsdlLocation>/wsdl/wfoutbound.wsdl</wsdlLocation> <executable>${env.WAS_HOME}/bin/jaxws/wsimport</executable> <packageName>com.ibm.cio.riker.dao.wsdl</packageName> <keep>true</keep> </configuration> <dependencies> <dependency> <groupId>com.sun.xml.ws</groupId> <artifactId>jaxws-tools</artifactId> <version>2.2.5</version> </dependency> </dependencies> </plugin> </plugins> </build> </project>
排查与解决方案
问题根源
AbstractMethodError本质是编译与运行时类版本不匹配或依赖冲突:
- 运行单元测试时,加载了Apache Wink的
ResponseBuilderImpl,但该类未实现javax.ws.rs.core.Response.ResponseBuilder中的status(int, String)方法 - 直接运行业务代码时使用的是Open Liberty自带的JAX-RS实现,而单元测试因新引入的
riker-common测试包间接引入了Apache Wink,导致依赖冲突
解决步骤
排查并排除冲突依赖
- 执行
mvn dependency:tree查看依赖树,确认org.apache.wink相关依赖的来源(大概率是riker-common引入) - 在
riker-common测试依赖中添加排除规则:<dependency> <groupId>com.ibm.cio.riker</groupId> <artifactId>riker-common</artifactId> <version>1.0.0</version> <scope>test</scope> <type>test-jar</type> <exclusions> <exclusion> <groupId>org.apache.wink</groupId> <artifactId>wink-common</artifactId> </exclusion> <!-- 其他Wink相关依赖也一并排除 --> </exclusions> </dependency>
- 执行
统一测试与运行时的JAX-RS实现
- 添加Open Liberty的JAX-RS测试依赖,让单元测试使用和服务器一致的实现:
<dependency> <groupId>io.openliberty</groupId> <artifactId>openliberty-jaxrs</artifactId> <version>你的Open Liberty版本</version> <scope>test</scope> </dependency> - 或使用
liberty-maven-plugin的测试目标运行单元测试,确保测试环境与服务器环境一致
- 添加Open Liberty的JAX-RS测试依赖,让单元测试使用和服务器一致的实现:
Mock Response对象(快速绕过方案)
- 如果不想调整依赖,可在单元测试中Mock
Response相关对象,避免调用真实JAX-RS实现:import static org.mockito.Mockito.*; import javax.ws.rs.core.Response; // 测试方法内 Response.ResponseBuilder mockBuilder = mock(Response.ResponseBuilder.class); Response mockResponse = mock(Response.class); when(Response.status(any(Response.Status.class))).thenReturn(mockBuilder); when(mockBuilder.entity(anyString())).thenReturn(mockBuilder); when(mockBuilder.build()).thenReturn(mockResponse);
- 如果不想调整依赖,可在单元测试中Mock
内容的提问来源于stack exchange,提问作者Paul M
相关产品推荐
相关产品推荐

