7

I need to write a test that mocks a instance of the java.lang.Class class. Is this possible via PowerMock?

I tried to do following:

PowerMock.createMock(Class.class);

And the result is:

java.lang.IllegalAccessError: java.lang.Class
    at sun.reflect.GeneratedSerializationConstructorAccessor12.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:40)
    at org.powermock.reflect.internal.WhiteboxImpl.newInstance(WhiteboxImpl.java:223)
    at org.powermock.reflect.Whitebox.newInstance(Whitebox.java:139)
    at org.powermock.api.easymock.PowerMock.doMock(PowerMock.java:2146)
    at org.powermock.api.easymock.PowerMock.createMock(PowerMock.java:89)

According to the documentation of PowerMock this should be possible but still I get this error.

Did someone manage to do this?

Edit: Why do I need this? In the tested coding there is following statement:

if (someObject.getClass().getName().equals(SOME_CLASS_NAME_THAT_I_DONT_HAVE_ACCESS_TO)) { ... do some stuff ... }

I need my test to reach the coding inside the "if" and I CANNOT provide even a mocked instance of the class that has the corresponding name.

As a workaround I can just create a class with the same name and package in the tests but it is ugly.

Edit2:

I tried also the suggestions from this link

import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.junit.Test;

@RunWith(PowerMockRunner.class)
@PrepareForTest({Test1.class})
public class Test1 {

    @Test
    public void test() {
        PowerMock.createMock(Class.class);
    }

}

And the result is the same: "java.lang.IllegalAccessError: java.lang.Class"

So as a final result - it seems that there is no way to create a mocked instance of java.lang.Class

Thank you

vap78
  • 1,029
  • 2
  • 11
  • 26

5 Answers5

2

According to this statement,

...

at java.lang.reflect.Constructor.newInstance(Constructor.java:526)

...

PowerMock (using Objenesis library) tries to instantiate java.lang.Class, which could be instantiated only by JVM. From docs:

Class has no public constructor. Instead Class objects are constructed automatically by the Java Virtual Machine as classes are loaded and by calls to the defineClass method in the class loader.

Shortly, I'm almost sure that it's not possible to make instance of java.lang.Class manually. Please correct me if I wrong.

By the way,

Unfortunately I need to change the return value of the getClass().getName()

Isn't mocking of getClass() method an option for you?

bsiamionau
  • 8,099
  • 4
  • 46
  • 73
  • Well I can mock the getClass() method but it needs to return an instance of the java.lang.Class class :) And since I cannot return a mocked instance there I cannot further mock the getName() method. The only thing I can do is just create an actual class with the same name and package and return it there - I mentioned this as an workaround. – vap78 Jan 15 '15 at 14:56
  • @vap78, I've found confirmation of my assumption, [see question 3.1](https://code.google.com/p/powermock/wiki/FAQ). btw, there are [link](https://code.google.com/p/powermock/source/browse/trunk/modules/module-test/mockito/junit4/src/test/java/samples/powermockito/junit4/system/SystemClassUserTest.java) to workaround is given. they suggest to use [PrepareForTest](http://powermock.googlecode.com/svn/docs/powermock-1.3.5/apidocs/org/powermock/core/classloader/annotations/PrepareForTest.html) annotation. – bsiamionau Jan 15 '15 at 15:17
  • @vap78, please, tell us if it helps – bsiamionau Jan 15 '15 at 15:18
  • I tried it and updated the question with the result. Unfortunately it looks like that it is just not possible to create a mocked class instance. – vap78 Jan 16 '15 at 18:19
2

You could use the JavaCompiler to compile a source file on the fly and a URLClassLoader to load the class instance.

Eg: How do I programmatically compile and instantiate a Java class?

Community
  • 1
  • 1
lance-java
  • 25,497
  • 4
  • 59
  • 101
  • A nice idea but generally I have a simpler solution - just create a real class with the same package and name. Your suggestion will be useful if creating such a class is not feasible due to some other conditions (like causing other issues at runtime etc) – vap78 Jan 16 '15 at 18:20
0

Why don't you load the class at runtime?

Class<?> cls = Class.forName("org.package.Class");
nrainer
  • 2,542
  • 2
  • 23
  • 35
  • Nope - I don't have access to this class when the unit tests are running. – vap78 Jan 17 '15 at 05:55
  • Why is that? Can you access the class via ``someObject.getClass().getClassLoader().loadClass("org.package.Class")``? (``someObject``is the one of your if condition) – nrainer Jan 17 '15 at 15:26
  • Because the class under test is part of a lower layer in an application server and it sees "someObject" (from the example) as a specific interface. At runtime I needed to change it to have a specific behavior when "someObject" is an instance of a specific implementation which is part of an upper layer. When the test case is executed I have only access to the lower layer. – vap78 Jan 18 '15 at 17:41
0
Class<? extends Collection> clazz = Collection.class;
Constructor c = PowerMockito.mock(Constructor.class);
Collection collection = mock(Collection.class);
    
PowerMockito.mockStatic(clazz);
when(clazz.getDeclaredConstructor(int.class)).thenThrow(NoSuchMethodException.class);
when(clazz.getDeclaredConstructor()).thenReturn(c);
when(c.newInstance()).thenReturn(collection);

verify(clazz).getDeclaredConstructor(int.class);
verify(clazz).getDeclaredConstructor();
vladkabat
  • 1
  • 3
-1

Are you sure you need to mock the class object? Is there a specific reason why you can't just do the following?

public class MyTest {
   public static class TestClass {
       public void doFoo() {}
   }

   @Test
   public void myTest() {
       doStuff(TestClass.class);
   }
}
lance-java
  • 25,497
  • 4
  • 59
  • 101
  • Unfortunately I need to change the return value of the getClass().getName() to something that I expect AND I cannot provide mock a instance of the class that I really expect because I don't have classpath to it (it is part of a higher layer). I'll add some clarification the question with the exact scenario. – vap78 Jan 15 '15 at 13:54