java.lang.LinkageError: ClassCastException

2022-01-14 00:00:00 mockito java classloader testng powermock

我确实遇到了一个非常烦人的 TestNG 和 RESTeasy 问题.

I do experience a really annoying problem with TestNG and RESTeasy.

我确实有一个类针对使用 RESTeasy 框架公开自身的 API 类运行多个测试.

I do have a class that runs several tests against an API class which uses the RESTeasy framework to expose itself.

但是,如果我让测试使用 maven (mvn test) 运行,则会出现以下异常:

However if I let the test run with maven (mvn test), then I get the following exception:

java.lang.LinkageError: ClassCastException: attempting to castjar:file:/C:/Users/rit/.m2/repository/org/jboss/resteasy/jaxrs-api/2.3.0.GA/jaxrs-api-2.3.0.GA.jar!/javax/ws/rs/ext/RuntimeDelegate.classtojar:file:/C:/Users/rit/.m2/repository/org/jboss/resteasy/jaxrs-api/2.3.0.GA/jaxrs-api-2.3.0.GA.jar!/javax/ws/rs/ext/RuntimeDelegate.class
at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:126)
at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:96)
at javax.ws.rs.core.Response$ResponseBuilder.newInstance(Response.java:394)
at javax.ws.rs.core.Response.status(Response.java:116)
at javax.ws.rs.core.Response.status(Response.java:130)
at com.pd.api.TokenAPI_V1.validateAccessToken(TokenAPI_V1.java:141)
at com.test.pd.api.TokenAPI_V1Test.testIfValidAccessTokenReturnsCorrectHTTPHeadersWhenTokenIsNotFound(TokenAPI_V1Test.java:359)

测试只是调用返回响应对象(来自 RESTeasy)的 API 对象的方法.作为测试框架,我确实使用了 TestNG.

The test does nothing more than calling a method of the API obejct which returns a Response object (from RESTeasy). As testing framework I do use TestNG.

测试方法

@Test
public void testIfValidAccessTokenReturnsCorrectHTTPHeadersWhenTokenIsNotFound() throws InvalidAccessTokenException {
    Mockito.when(tokenService.validateAccessToken(TestConstants.ACCESS_TOKEN)).thenThrow(new InvalidAccessTokenException());

    Response response = tokenAPI_v1.validateAccessToken(TestConstants.ACCESS_TOKEN, TestConstants.USER_AGENT);
    assert "no-store".equals(response.getMetadata().getFirst("Cache-Control"));
    assert "no-cache".equals(response.getMetadata().getFirst("Pragma"));
}

问题描述

看起来 RESTeasy 框架在不同的类加载器中加载了 RuntimeDelegate.如果我看一下源代码,那么 RuntimeDelegate 有以下方法(涵盖第 126 行): RuntimeDelegate.java.

It looks like the RESTeasy framework loads the RuntimeDelegate in a different class loader. If I take a look at the source code, then there is the following method at the RuntimeDelegate (which covers line 126): RuntimeDelegate.java.

所以与错误相关的主要语句是instanceof check:

So the main statement that is related to the error is the instanceof check:

if (!(delegate instanceof RuntimeDelegate))

如果我检查委托实例的类加载器与 RuntimeDelegate 的类加载器,则会得到以下输出:

If I check the classloader of the delegate instance vs the classloader of the RuntimeDelegate, then I get the following output:

delegate.getClass().getClassLoader() -> org.powermock.core.classloader.MockClassLoader@31e46a68

RuntimeDelegate.class.getClassLoader() -> sun.misc.Launcher$AppClassLoader@3c0fabe9

我知道这当然行不通,但我想知道为什么 RESTeasy 的东西被加载到 MockClassLoader 而不是另一个.特别是因为我不模拟经过测试的 TokenAPI.

I am aware of that this of course doesn't work but I wonder why the RESTeasy stuff is loaded in the MockClassLoader and not in the other one. Especially as I don't mock the TokenAPI which gets tested.

奇怪的事实

奇怪的是,当我从 IntelliJ 运行测试时(我选择只运行包含产生错误的方法的给定类的所有测试),然后它就会运行.看起来它与 mvn test 运行来自 maven 项目的所有测试这一事实有某种关系(或者至少我猜是这样).

The strange thing is, that when I run the tests out of IntelliJ (I choose only to run all tests from the given class which contains the method that produces the error), then it runs through. It looks like it is somehow related to the fact that mvn test runs all tests from the maven project (or at least that's what I guess).

推荐答案

很遗憾,我不能告诉你为什么会这样,但我可以告诉你如何解决这个问题.

Unfortunately I can't tell you why this happened, but I can tell you how to get around this issue.

问题是,PowerMockito 扫描了类路径并添加了 RESTeasy 类(位于包 'javax.ws.*' 中.因此,上面提到的 RuntimeDelegate 由 PowerMockito 类加载器加载并导致后来的问题,该类与来自不同类加载器的类进行了比较.

The problem was, that PowerMockito scanned tha class path and also added the RESTeasy classes (which are located within the package 'javax.ws.*'. Therefor the above mentioned RuntimeDelegate was loaded by the PowerMockito classloader and caused later the issue, that the class was compared against one from a different classloader.

要解决此问题,请告诉 PowerMockito 在扫描类时忽略 javax.ws 包:

To get around this issue, tell PowerMockito to ignore the javax.ws package when scanning for classes:

@PowerMockIgnore({"javax.ws.*"})

相关文章