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.
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
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)
Thank you so much~ it works!
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)
Are you using version 1.6.2? If not then try to upgrade.
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
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.
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
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.
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.
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.
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.
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
Interesting, could you add it as an issue at the issue tracker?
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.
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
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
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
great thanks :) solved my problem of running powermock and spring dependency injection.
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)