Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cannot call mockito from a class instantiated in custom classloader (possible regression) #2304

Open
manolo opened this issue May 20, 2021 · 3 comments

Comments

@manolo
Copy link

manolo commented May 20, 2021

We had a test able to call Mockito.mock from classes created in custom classloaders working in mockito 1.x, when upgrading to 3.x it fails.

The test code demonstrating the issue is:

public class MyTest {
    
    public static class MyClass {
        public static class MyOtherClass {
        }

        public void initialize() {
            MyOtherClass mock = Mockito.mock(MyOtherClass.class);
        }
    }

    @Test
    public void myTest() throws Exception {
        URLClassLoader customLoader = new URLClassLoader(getClasspathURLs(), null);
        
        Class<?> clz = customLoader.loadClass(MyClass.class.getName());
        Method mtd = clz.getMethod("initialize");
        
        Object inst = clz.newInstance();
        
        mtd.invoke(inst, new Object[0]);
    }
    private URL[] getClasspathURLs() {
        return Arrays.stream(
                System.getProperty("java.class.path").split(File.pathSeparator))
                .map(s -> {
                    try {
                        return new File(s).toURI().toURL();
                    } catch (MalformedURLException e) {
                        throw new RuntimeException(e);
                    }
                }).collect(Collectors.toList()).toArray(new URL[0]);
    }
}

The exception I got is:

Caused by: org.mockito.exceptions.base.MockitoException: 
ClassCastException occurred while creating the mockito mock :
  class to mock : '...MyTest.MyClass.MyOtherClass', loaded by classloader : 'java.net.URLClassLoader@e73f9ac'
  created class : 'org.mockito.codegen.MyOtherClass$MockitoMock$1564550697', loaded by classloader : 'net.bytebuddy.dynamic.loading.MultipleParentClassLoader@75d4a5c2'
  proxy instance class : 'org.mockito.codegen.MyOtherClass$MockitoMock$1564550697', loaded by classloader : 'net.bytebuddy.dynamic.loading.MultipleParentClassLoader@75d4a5c2'
  instance creation by : ObjenesisInstantiator

You might experience classloading issues, please ask the mockito mailing-list.

	at ....MyTest$MyClass.initialize(MyTest.java:23)
	... 31 more
Caused by: java.lang.ClassCastException: org.mockito.codegen.MyOtherClass$MockitoMock$1564550697 cannot be cast to org.mockito.internal.creation.bytebuddy.MockAccess
	at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:49)

@TimvdLippe
Copy link
Contributor

Could this require a similar solution to the one mentioned in #2303 ?

@charlesmunger
Copy link
Contributor

I believe this is actually a different failure; the MultipleParentsClassloader needs to be seeded with a set of existing classes (MockAccess, MyOtherClass, and any interfaces added to the mock), so that it knows which of the overlapping classloaders to use for MyOtherClass. Right now it's ordering dependent on what gets passed to MultipleParentsClassLoader.

@TimvdLippe
Copy link
Contributor

@manolo Can you please check if you are still running into this issue on the latest version of Mockito? If yes, are you interested in contributing a fix? The relevant code is at

and we have various regression tests in https://github.com/mockito/mockito/tree/main/src/test/java/org/mockitousage/bugs/creation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants