Using another JUnit Runner with PowerMock

PowerMock 1.6.0 was released a couple of days ago and the main new feature expect for better support for Mockito 1.10.x is the ability to combine the PowerMockRunner with another JUnit runner without using any of the experimental JUnit Rules. This is achieved by using the new PowerMockRunnerDelegate annotation. For example you can delegate to Enclosed Runner, Parameterized Runner or the SpringJUnit4ClassRunner and still use the PowerMock functionality.

SpringJUnit4ClassRunner Example

Here’s a (contrived) example of combining PowerMock and the SpringJUnit4ClassRunner for integration testing “MyBean”.

First consider the code under test:

import org.springframework.stereotype.Component;

@Component
public class MyBean {

    public Message generateMessage() {
        final long id = IdGenerator.generateNewId();
        return new Message(id, "My bean message");
    }
}
public class IdGenerator {

	/**
	 * @return A new ID based on the current time.
	 */
	public static long generateNewId() {
		return System.currentTimeMillis();
	}
}

To test this inside a running Spring environment you can use the PowerMockRunnerDelegate and delegate to to the SpringJUnit4ClassRunner:

package org.powermock.examples.spring.mockito;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import powermock.examples.spring.IdGenerator;
import powermock.examples.spring.Message;
import powermock.examples.spring.MyBean;

import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.*;
import static org.powermock.api.support.membermodification.MemberMatcher.method;
import static org.powermock.api.support.membermodification.MemberModifier.stub;
import static org.powermock.api.support.membermodification.MemberModifier.suppress;

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:/example-context.xml")
@PrepareForTest(IdGenerator.class)
public class SpringExampleTest {

    @Autowired
    private MyBean myBean;

    @Test
    public void mockStaticMethod() throws Exception {
        // Given
        final long expectedId = 2L;
        mockStatic(IdGenerator.class);
        when(IdGenerator.generateNewId()).thenReturn(expectedId);

        // When
        final Message message = myBean.generateMessage();

        // Then
        assertEquals(expectedId, message.getId());
        assertEquals("My bean message", message.getContent());
    }
}

As you can also see in this example you can still apply annotations from the delegating framework in the test (such as ContextConfiguration in this example).

Conclusion

Using other JUnit runners in combination with the PowerMockRunner opens up a whole new world of possibilities. Even though you could use any of the three PowerMock provided JUnit Rules to accommodate this in a previous version they could be fragile and were not as capable as the JUnit Runner. A big thanks goes out to Henrik Kaipe for providing this excellent pull request.

This Post Has 20 Comments

  1. Ash

    I have updated my project to use powerMock 1.6.0 and removed Junit Rule to use @PowerMockRunnerDelegate, but the annotation seems to be ignored. I tried the same example as yours but the autowired Object were null. I removed them and tried with an empty body test method and referenced an unexisting file in @ContextConfiguration, but there was no fail saying the configuration file was not found.
    I used the same version of mockito and Junit comming by default with powerMock 1.6.0 (i.e. Junit 4.11 and mockito 1.10.8) and i’m using spring 3.1.4

  2. Ash

    I see the problem, the annotion @PowerMockRunnerDelegate was on an abstract class i have for some of my unit tests that need a mocked DB. But the annotation is ignored when running a test in the testClasses that inherit this abstractClass (unlike @PrepareForTest that works fine). Setting @PowerMockRunnerDelegate in each child class doen’t work, an exception is thrown by org.junit.runners.model.TestClass “Test class can only have one constructor”

    Here is the stack trace:

    java.lang.IllegalArgumentException: Test class can only have one constructor
    at org.junit.runners.model.TestClass.(TestClass.java:40)
    at org.junit.runners.ParentRunner.(ParentRunner.java:75)
    at org.junit.runners.BlockJUnit4ClassRunner.(BlockJUnit4ClassRunner.java:57)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.(SpringJUnit4ClassRunner.java:104)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.powermock.reflect.internal.WhiteboxImpl.createInstance(WhiteboxImpl.java:1428)
    at org.powermock.reflect.internal.WhiteboxImpl.invokeConstructor(WhiteboxImpl.java:1301)
    at org.powermock.reflect.Whitebox.invokeConstructor(Whitebox.java:497)
    at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$1.call(DelegatingPowerMockRunner.java:96)
    at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$1.call(DelegatingPowerMockRunner.java:93)
    at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.withContextClassLoader(DelegatingPowerMockRunner.java:127)
    at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.createDelegate(DelegatingPowerMockRunner.java:92)
    at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.(DelegatingPowerMockRunner.java:60)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:156)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.createDelegatorFromClassloader(JUnit4TestSuiteChunkerImpl.java:40)
    at org.powermock.tests.utils.impl.AbstractTestSuiteChunkerImpl.createTestDelegators(AbstractTestSuiteChunkerImpl.java:244)
    at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.(JUnit4TestSuiteChunkerImpl.java:61)
    at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.(AbstractCommonPowerMockRunner.java:32)
    at org.powermock.modules.junit4.PowerMockRunner.(PowerMockRunner.java:34)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:29)
    at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:21)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
    at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:31)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.(JUnit4TestReference.java:33)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestMethodReference.(JUnit4TestMethodReference.java:25)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:54)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:452)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

  3. Chuah Jun Sheng

    Thank you so much~ it works!

  4. Kamlesh

    Im also getting the same error. Is there any update on this
    java.lang.IllegalArgumentException: Test class can only have one constructor
    at org.junit.runners.model.TestClass.(TestClass.java:40)
    at org.junit.runners.ParentRunner.(ParentRunner.java:75)
    at org.junit.runners.BlockJUnit4ClassRunner.(BlockJUnit4ClassRunner.java:57)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.(SpringJUnit4ClassRunner.java:104)

    1. Johan Haleby

      Are you using version 1.6.2? If not then try to upgrade.

  5. Emat

    Thanks Johan Haleby, this method worked just fine.
    Unfortunately, a colleague is focusing on the phrase in your post about “experimental JUnit Rules” and trying to point how PowerMockRule was not supposed to be used in our project in the first place.
    After your post, it looks like there were 2 releases, is the JUnit Rule still experimental?
    I couldn’t find any reference of PowerMockRule being experimental except in your post, which I unfortunately missed. Is there a reliable way to check which which other classes and features in PowerMock are experimental?
    like this one is marked (experimental) https://code.google.com/p/powermock/wiki/PowerMockAgent
    this one is not marked https://code.google.com/p/powermock/wiki/PowerMockRule

    1. Johan Haleby

      I would say that if it works for your use case then it’s fine to use it. But if you run into problems then I would suggest another approach so that you don’t complicate the test too much (for example with a lot of PowerMockIgnore’s). I would however look into the delegating runner before I try the rules.

  6. Andreas Jonsson

    Hi Johan Haleby,

    When combining PowerMockRunner & Parameterized (see code below) we get the following “error” (tests are still passing) in the log:

    “Detected late test-suite preparation of already initiated test-class com.xyz.util.date.DateUtilTest”

    Do you know how to solve this case? I know it’s printed by “org.powermock.core.testlisteners.GlobalNotificationBuildSupport” but not sure if there are any workarounds.

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({TimeZoneUtil.class, DateUtil.class})
    @PowerMockRunnerDelegate(Parameterized.class)
    public class DateUtilTest {..

    Thanks
    Andreas

  7. kiran

    i am getting the same error
    Do you know how to solve this case? I know it’s printed by “org.powermock.core.testlisteners.GlobalNotificationBuildSupport” but not sure if there are any workarounds.

  8. Rob Cash

    This was working fine for me with PowerMock 1.6.2, JUnit 4.10, Spring 3.1.x, and JDK 1.7. When I upgraded to Spring 4.1.x and JDK 8, I’ve started getting errors for each unit test:

    Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
    Notifications are not supported for behaviour ALL_TESTINSTANCES_ARE_CREATED_FIRST
    Notifications are not supported when all test-instances are created first!

    Any idea how to reconfigure this so that the error messages go away? The tests appear to be working as intended.

    1. Rob Cash

      I believe I found the source of my problem. My test class was annotated with @PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class) and it also extended AbstractJUnit4SpringContextTests. This was causing the tests to be initialized twice, which the PowerMock NotificationBuilder didn’t like too much.

  9. Michael Rountree

    I get an error of java.lang.IllegalArgumentException: Unable to determine method-name from description=shouldReturnValid(com.metlife.eservices.webservices.security.command.SecurityServiceCommandTest$hasValidSession$WhenSessionNotNull$WhenUidNotNull$UidEqualUserCredentialsUid); – ignored

    My code is
    @RunWith(PowerMockRunner.class) //PowerMockRunner or MockitoJUnitRunner
    @PowerMockRunnerDelegate(NestedRunner.class)
    //@PrepareForTest(SecurityServiceCommand.class)
    public class SecurityServiceCommandTest {

    public class hasValidSession {

    public class WhenSessionNotNull{

    public class WhenUidNotNull{

    public class UidEqualUserCredentialsUid{

    @Test
    public void shouldReturnValid() {
    assertTrue(true);
    }

    When combining the PowerMockRunner with NestedRunner. The tests seem to still run just fine, but I’m not sure what the real error is.

  10. Velmurugan

    This issue due to below line in Categories.CategoryFilter file under method hasCorrectCategoryAnnotation

    if (fIncluded == null || fIncluded.isAssignableFrom(each)) fIncluded.isAssignableFrom(each)) – this will fails eventhough the class type is same or under inheritance because ‘fIncluded’ is loaded by Categories classloader and ‘each’ is loaded by PowerMockRunner class loader. Probably we need fix this logic

    1. Johan Haleby

      Interesting, could you add it as an issue at the issue tracker?

  11. zveratko

    I’m using @PowerMockRunnerDelegate(UnitilsBlockJUnit4ClassRunner.class). It works somehow, but I have two problems.
    1) I’m unable to run only one test from the IDE(intelij) always all tests are executed
    2) @DataSet(…) annotation seems to be ignored

    It can be Unitils specific problem, but I don’t know how to confirm it.

  12. Gareth Sullivan

    This was a great find for me, but bear in mind that Powermock loads classes in its own class loader. We encountered an issue where the test behviour was differing to running the production code manually. Specifically the XML output from JaxB had differences in namespaces, as if the package-info.java files were being ignored when run using the PowerMock test delegating to SpringJunit4ClassRunner.

    After much debugging it turned ou the issue was that the annotation class was being loaded by the powermock class loader and so when the JAXB (loaded by system class loader) classes were invoked – the annotation was not found and so package-info was effectively ignored.

    We used the

    @PowerMockIgnore(“javax.xml.bind.annotation.*”)

    annotation to specify that all classes under the specified package be loaded by the system class loader.

    TL;DR – use the @PowerMockIgnore annotation to force classes to be loaded by system class loader instead of the PowerMock class loader

  13. Anoop Singh

    Hi Johan Haleby, All,

    Could you please provide me the jar version details which is working for above example:

    Mockito version?
    Powermock version?
    Spring Version?
    JUnit version?

    I have tried with powermock version 1.6.2 but getting the following exception:

    java.lang.IllegalArgumentException: Test class can only have one constructor

  14. Ravish

    java.lang.NoClassDefFoundError: org/powermock/tests/utils/RunnerTestSuiteChunker
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at java.lang.Class.getDeclaredConstructors0(Native Method)
    at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
    at java.lang.Class.getConstructor0(Class.java:3075)
    at java.lang.Class.getConstructor(Class.java:1825)
    at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
    at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:33)
    at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:36)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.(JUnit4TestReference.java:33)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestMethodReference.(JUnit4TestMethodReference.java:25)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:54)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
    Caused by: java.lang.ClassNotFoundException: org.powermock.tests.utils.RunnerTestSuiteChunker
    at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    … 31 more

  15. Shilan

    great thanks :) solved my problem of running powermock and spring dependency injection.

  16. Rajiv Sikdar

    I am getting following exception while using PowerMock. It seems to be version mismatch/incompatibility?
    My Class is:
    @ActiveProfiles(“jtest”)
    @RunWith(PowerMockRunner.class)
    @PowerMockRunnerDelegate(SpringRunner.class)
    @WebMvcTest(value = OperationController.class, secure = false)
    public class OperationControllerTest {
    // Test cases
    }
    POM dependency added:

    org.powermock
    powermock-module-junit4
    2.0.2
    test

    org.powermock
    powermock-api-support
    2.0.2
    test

    org.powermock
    powermock-api-mockito
    1.7.4
    test

    org.mockito
    mockito-all
    1.10.19
    test

    java.lang.AbstractMethodError: org.powermock.api.mockito.internal.mockmaker.PowerMockMaker.isTypeMockable(Ljava/lang/Class;)Lorg/mockito/plugins/MockMaker$TypeMockability;
    at org.mockito.internal.util.MockUtil.typeMockabilityOf(MockUtil.java:29)
    at org.mockito.internal.util.MockCreationValidator.validateType(MockCreationValidator.java:22)
    at org.mockito.internal.creation.MockSettingsImpl.validatedSettings(MockSettingsImpl.java:232)
    at org.mockito.internal.creation.MockSettingsImpl.build(MockSettingsImpl.java:226)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:64)
    at org.mockito.Mockito.mock(Mockito.java:1855)
    at org.springframework.boot.test.mock.mockito.MockDefinition.createMock(MockDefinition.java:157)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.createMock(MockitoPostProcessor.java:222)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.registerMock(MockitoPostProcessor.java:192)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.register(MockitoPostProcessor.java:174)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.postProcessBeanFactory(MockitoPostProcessor.java:144)
    at org.springframework.boot.test.mock.mockito.MockitoPostProcessor.postProcessBeanFactory(MockitoPostProcessor.java:131)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:282)
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:170)
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:694)

Leave a Reply