Experimento un problema realmente molesto con TestNG y RESTeasy.java.lang.LinkageError: ClassCastException
Tengo una clase que ejecuta varias pruebas contra una clase API que utiliza el marco RESTeasy para exponerse.
Sin embargo si dejo que la prueba de funcionamiento con Maven (mvn test), entonces consigo la siguiente excepción:
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)
La prueba no hace más que llamar a un método de la obejct API que devuelve un objeto Respuesta (de RESTeasy). Como marco de prueba, sí uso TestNG.
Método de prueba
@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"));
}
Descripción del problema
Parece que el marco RESTeasy carga el RuntimeDelegate en un cargador de clases diferentes. Si eché un vistazo al código fuente, existe el siguiente método en RuntimeDelegate (que cubre la línea 126): RuntimeDelegate.java.
Así que la declaración principal que se relaciona con el error es el cheque instanceof:
if (!(delegate instanceof RuntimeDelegate))
Si puedo comprobar el cargador de clase de la instancia de delegado vs el cargador de clase de la RuntimeDelegate, entonces me sale el siguiente resultado:
delegate.getClass().getClassLoader() -> [email protected]
RuntimeDelegate.class.getClassLoader() -> [email protected]
Soy consciente de que esto, por supuesto, no funciona, pero me pregunto por qué las cosas RESTeasy se cargan en el MockClassLoader y no en el otro. Especialmente porque no me burlo del TokenAPI que se prueba.
hechos extraños
Lo extraño es que cuando ejecuto las pruebas de IntelliJ (I sólo podrán optar a ejecutar todas las pruebas de la clase dada que contiene el método que produce el error), entonces Corre a través de. Parece que de alguna manera está relacionado con el hecho de que mvn test ejecuta todas las pruebas del proyecto maven (o al menos eso es lo que supongo).
Al menos me di cuenta ahora cómo hacer que las pruebas se ejecuten nuevamente: Una de las pruebas utiliza PowerMockito y como se indica en el cargador de clases anterior, de alguna manera PowerMockito cargó las clases debajo del paquete jagax.ws. Para desactivar PowerMockito cargar las clases, he añadido el paquete javax.ws a la anotación PowerMockitoIgnore: ({ "javax.ws *"}) @PowerMockIgnore Todavía no entiendo por qué el la prueba que falla utiliza el cargador de clases provisto por PowerMockito. ¿Esto está relacionado con TestNG? – rit
Hola, realmente depende de si el cargador de clases del sistema cargó primero esas clases antes o no. Su código sin duda hace referencia a algunas clases de JAX-WS, que podrían hacer referencia a otras classe en JAX-WS. La JVM realiza el enlace más tarde para evitar una carga innecesaria durante el arranque. Entonces, cuando estas clases se usan por primera vez, las carga el cargador de clases PowerMock, pero cuando se inicia RESTeasy se carga usando el cargador de clases del sistema, luego se comparan los tipos (los de instancia) son diferentes, porque se cargaron en diferentes cargador de clases Su solución es la adecuada. – Brice
Si su solución funciona y es la correcta, puede responder su propia pregunta. – Luciano