Exception of type java.lang.IllegalStateException expected but was not thrown. Instead an exception of type class org.mockito.cglib.core.CodeGenerationException with message 'java.lang.reflect.InvocationTargetException-->null' was thrown.But let's start from the beginning. We have a spring web application that contains only 2 classes. First one is a standard session scoped component:
@Component @Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS) public class MySessionComponent { public void doError() { throw new IllegalStateException(); } public void doNothing() {} }Nothing new, right? Second component is a standard singleton:
@Component public class MySingleton { @Autowired MySessionComponent mySessionComponent; public void sampleAction() { mySessionComponent.doNothing(); } }That's the whole application. Now let's test it.
import org.junit.Test; import static com.googlecode.catchexception.apis.CatchExceptionBdd.*; public class MySessionComponentTest { @Test public void test() { when(new MySessionComponent()).doError(); thenThrown(IllegalStateException.class); } }I use catch exception v1.0.4 and test passes. Now, let's do an integration test:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @WebAppConfiguration public class MySingletonTest { @Configuration @ComponentScan static class TestAppContext {} // just registers 2 components @Autowired MySingleton myController; @Test public void test() { myController.sampleAction(); } }All tests pass. And now, let's suppose that we need, for whatever reason, to combine those tests:
@Test public void test() { myController.sampleAction(); when(new MySessionComponent()).doError(); thenThrown(IllegalStateException.class); }And we get the exception. WTF? After some time spent with debugger I found
InvocationTargetException
thrown inside org.mockito.cglib.core.AbstractClassGenerator
. The problem is there is no cause nor detailed message and therefore it's not propagated anywhere so you can't find the real reason in any logs. However this exception has target
field and there we can find:java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "MySessionComponent$$FastClassByCGLIB$$441a78f3"At first spring creates proxy for
MySessionComponent
in order to autowire beans with different scopes. Then catch-exception tries to create proxy for the same class. It seems that both frameworks generates the same name for the class and two classes with same name are not allowed within one classloader.When we change the order of method invocations (the order of creating proxies)
@Test public void deleteEbook() { when(new MySessionComponent()).doError(); thenThrown(IllegalStateException.class); myController.sampleAction(); }spring throws an exception but now you can see the real cause in the stacktrace:
org.springframework.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237) ... Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "MySessionComponent$$FastClassByCGLIB$$441a78f3"Btw, this behavior is strange because cglib claims it can detect name clashes. Maybe it's about repackaging cglib in almost every framework?
1 komentarze :
I've upload a fix for this case that has been recently uploaded in catch-exception version 1.1.0. :)
Post a Comment