This is a rather short article explaining how to solve some of the issues you may encounter when using PowerMockito with Spring Boot.

For various reasons, you may be required to mock method calls on objects which are created ad-hoc in other objects (not injected via Spring Dependency Injection)

Normally, you would use PowerMock for this. If you don’t know what PowerMock is, here is a good introduction:

http://www.baeldung.com/intro-to-powermock

PowerMock works by instrumenting the method calls at runtime, catching the calls and manipulating them as needed. For this, you need to change the Runner class for the unit test. This is done with the @RunWith() annotation on your test class

@RunWith(PowerMockRunner.class)

But what if your test needs the Spring Context up and running before testing?

Luckily, PowerMock supports another annotation to make this possible.

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)

So let’s write a test class and put this to work:

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest(DatabaseCreateReplJob.class)
@SpringBootTest( webEnvironment = WebEnvironment.DEFINED_PORT )
public class DatabaseCreateReplTest{
    
    @Mock
    SystemFacade sysFacade; 
    
    @Test
    public void test(){         
        PowerMockito.whenNew(SystemFacade.class).withAnyArguments().thenReturn(sysFacade);
        PowerMockito.when( sysFacade.createReplDBLink() ).thenReturn(true);
            
        DatabaseCreateReplJob job = new DatabaseCreateReplJob();
        int result = job.call();
        assertThat(result).isEqualTo(0);
    }   
}

The code is very simple but demonstrates the concept.

DatabaseCreateReplJob is an object which inside call(), it calls a method on the sysFacade object. Because this is a call to an external resource (such as a database or something else) we need to mock the response to sysFacade.createReplDBLink().

If we run the code as it is now, we get an error like this:

Caused by: java.lang.LinkageError: loader constraint violation: loader (instance of org/powermock/core/classloader/MockClassLoader) previously initiated loading for a different type with name "javax/management/MBeanServer"
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at org.powermock.core.classloader.MockClassLoader.loadUnmockedClass(MockClassLoader.java:262)
    at org.powermock.core.classloader.MockClassLoader.loadModifiedClass(MockClassLoader.java:206)
    at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass1(DeferSupportingClassLoader.java:89)
    at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:79)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
    at java.lang.Class.privateGetPublicMethods(Class.java:2902)
    at java.lang.Class.privateGetPublicMethods(Class.java:2917)
    at java.lang.Class.privateGetPublicMethods(Class.java:2917)
    at java.lang.Class.privateGetPublicMethods(Class.java:2917)
    at java.lang.Class.getMethods(Class.java:1615)

This happens because PowerMockRunner uses its own classloader to load classes and it interferes with the one in SpringRunner.

This is only an error which might occur. Depending on what your application is loading, different errors can happen.

The solution to all of this is to indicate to PowerMock which classes to ignore when classloading.

This is done with the @PowerMockIgnore annotation.

Example:

@PowerMockIgnore({"javax.*.*", "com.sun.*", "org.xml.*"})

If we add this one annotation to the test class we will have a successful test.

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0