Powermock: Newer versions do not work with Spock

Created on 10 Mar 2017  路  5Comments  路  Source: powermock/powermock

What steps will reproduce the problem?
Run a Spock test with a PowerMockRule

@PrepareForTest([Class1, Class2])
class MyTest extends Specification {
    @Rule PowerMockRule powerMockRule
}

What is the expected output?
Test runs with the ability to use power mock
What do you see instead?
A NullPointerException

java.lang.NullPointerException
at org.powermock.modules.junit4.rule.PowerMockStatement.(PowerMockRule.java:70)
at org.powermock.modules.junit4.rule.PowerMockRule.apply(PowerMockRule.java:44)
at org.spockframework.runtime.extension.builtin.MethodRuleInterceptor.intercept(MethodRuleInterceptor.java:37)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

What version of the product are you using?
1.6.6
On what operating system?
MacOS.
Please provide any additional information below.

This worked in 1.6.4 but broke in 1.6.5 and 1.6.6

All of our test dependencies are:

    testCompile 'junit:junit:4.12'

    testCompile 'org.hamcrest:hamcrest-core:1.3'
    testCompile 'org.hamcrest:hamcrest-library:1.3'
    testCompile 'org.codehaus.groovy:groovy:2.4.6:grooid'
    testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
    testCompile 'cglib:cglib-nodep:2.2'
    testCompile 'org.objenesis:objenesis:1.2'
    testCompile 'org.json:json:20140107'
    testCompile 'org.mockito:mockito-core:1.10.19'
    testCompile "org.powermock:powermock-module-junit4:1.6.6"
    testCompile "org.powermock:powermock-module-junit4-rule:1.6.6"
    testCompile "org.powermock:powermock-classloading-xstream:1.6.6"
    testCompile("org.powermock:powermock-api-mockito:1.6.6") {
        // creates conflict with hamcrest
        exclude group: 'org.mockito'
    }
    testCompile 'com.google.mockwebserver:mockwebserver:20130706'
    testCompile('org.robospock:robospock:1.0.1') {
        exclude group: 'org.spockframework'
    }
bug regression

Most helpful comment

I couldn't get any of the suggestions above to work, but I did find something that worked.

I replaced using PowerMockRule with using PowerMockRunnerDelegate.

@PrepareForTest(NativeBuffer)
@RunWith(PowerMockRunner)
@PowerMockRunnerDelegate(Sputnik)
@Unroll
class NativeBufferTest extends Specification {
}

This worked in both 1.6.6 and 1.7.0.
This also seems to have fixed some problems I was having in 1.6.4.

All 5 comments

I experienced the same problem. It seems to be working for me now by adding and rearranging the dependencies in the pom file according to this Stack Overflow answer: http://stackoverflow.com/a/25626601/2148380

I found the same issue working around with spock and powermock 1.7.0 and fix it with a patch.
The reason is that the Spock create test specification with it's own protocol.
Spock test (feature) methods with annotation @FeatureMeta could not be pass test of org.powermock.modules.junit4.common.internal.impl.JUnit4TestMethodChecker.
A SpockTestMethodChecker does works.

public class PowerMockRuleTestSuiteChunker extends AbstractCommonTestSuiteChunkerImpl {

    public PowerMockRuleTestSuiteChunker(Class testClass) throws Exception {
        super(testClass);
    }

    @Override
    public boolean shouldExecuteTestForMethod(Class<?> testClass, Method potentialTestMethod) {
        return new SpockTestMethodChecker(testClass, potentialTestMethod).isTestMethod();
    }

}

public class SpockTestMethodChecker extends JUnit4TestMethodChecker {
    private final Class<?> testClass;
    private final Method potentialTestMethod;

    public SpockTestMethodChecker(Class<?> testClass, Method potentialTestMethod) {
        super(testClass,potentialTestMethod);
        this.testClass = testClass;
        this.potentialTestMethod = potentialTestMethod;
    }

    @Override
    public boolean isTestMethod() {
        return isJUnit3TestMethod() || isJUnit4TestMethod() || isSpockFeatureMethod();
    }
    protected boolean isSpockFeatureMethod() {
        return potentialTestMethod.isAnnotationPresent(FeatureMetadata.class);
    }
}

Codes about SpockPowerMockRule are not listed.

At the same time, Spock replace @Before with void setup() , a private java method (
as same as cleanup()).
Statements apply to feature method will run setup method as pre action, and raise an exception of private method invocation, Although setup and cleanup method has been changed to accessible while creating specifications (It is a SPOCK BUG)
I found that org.powermock.modules.spock.rule.PowerMockStatement evalute spock test method under it's own classloaderExecutor, which restore all opened setup method to private.
This patch works for me

    public PowerMockStatement(final Statement featureInvokeStatement,
                              final Specification target, TestChunk testChunk,
                              MockPolicyInitializer mockPolicyInitializer) {
        this.fNext = new Statement() {
            @Override
            public void evaluate() throws Throwable {
                List<MethodInfo> allMethods = new LinkedList<MethodInfo>();


   allMethods.addAll(target.getSpecificationContext().getCurrentSpec().getSetupMethods());
                allMethods.addAll(target.getSpecificationContext().getCurrentSpec().getCleanupMethods());

                for(MethodInfo m: allMethods){
                    m.getReflection().setAccessible(true);
                }

                //do real statement invoke
                featureInvokeStatement.evaluate();
            }
        };
//some other code
}

As it is a ClassLoader issue, patches on class loader code maybe a better solution

I couldn't get any of the suggestions above to work, but I did find something that worked.

I replaced using PowerMockRule with using PowerMockRunnerDelegate.

@PrepareForTest(NativeBuffer)
@RunWith(PowerMockRunner)
@PowerMockRunnerDelegate(Sputnik)
@Unroll
class NativeBufferTest extends Specification {
}

This worked in both 1.6.6 and 1.7.0.
This also seems to have fixed some problems I was having in 1.6.4.

There is another solution that replacing powermock-module-junit4-rule with powermock-module-junit4-rule-agent ,which cause a very slower bootstraping.

Just as a summary for future readers, what @jorunfa and @hitsmaxft suggest is actually the same. The rule class in powermock-module-junit4-rule-agent and powermock-module-junit4-rule have the same FQDN, so what @hitsmaxft (or actually the SO answer) does is to put powermock-module-junit4-rule-agent before powermock-module-junit4-rule in the classpath and so the other rule implementation is used that works differently and does not have this NPE problem.

Actually for me this doesn't work either, as I get verify errors when I use the agent, no matter whether directly or via rule and with or without JaCoCo.

Doing what @mkrussel77 suggested actually works, but only kind of, because the PowerMock test notification code does not cope well with Spock specifications, so you will always get messages to stderr if you are following that approach, while at least the currently (2.0.2) shipped PowerMock test listeners are not affected by these stderr messages as they don't need the concrete test method being supplied.

But actually for me currently this option is the only one with which I at least get it working with my Spock test. :-(

Was this page helpful?
0 / 5 - 0 ratings