8

According to the PowerMock docs, I should be able to run using a PowerMockRule instead of @RunWith(PowerMockRunner.class) and get the same results.

I seem to have found a case where this isn't true.

The below sample runs fine:

package com.test.powermockstatics;

import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

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

final class FinalClassWithStaticCall {
  public static int getIntStatic() {
    return 1;
  }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClassWithStaticCall.class)
public class TestStaticMockingWithoutPowerMockRunner {
  @Test
  public void testStaticCall() {
    mockStatic(FinalClassWithStaticCall.class);
    when(FinalClassWithStaticCall.getIntStatic()).thenReturn(2);

    assertEquals(FinalClassWithStaticCall.getIntStatic(), 2);
  }
}

But when switched to a rule like so:

package com.test.powermockstatics;

import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;

import org.junit.Rule;
import org.junit.Test;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.agent.PowerMockAgent;
import org.powermock.modules.junit4.rule.PowerMockRule;

final class FinalClassWithStaticCall {
  public static int getIntStatic() {
    return 1;
  }
}

@PrepareForTest(FinalClassWithStaticCall.class)
public class TestStaticMockingWithoutPowerMockRunner {
  static {
    PowerMockAgent.initializeIfNeeded();
  }

  @Rule
  public PowerMockRule rule = new PowerMockRule();

  @Test
  public void testStaticCall() {
    mockStatic(FinalClassWithStaticCall.class);
    when(FinalClassWithStaticCall.getIntStatic()).thenReturn(2);

    assertEquals(FinalClassWithStaticCall.getIntStatic(), 2);
  }
}

I get the following exception:

java.lang.IllegalArgumentException: Cannot subclass final class class com.test.powermockstatics.FinalClassWithStaticCall at org.mockito.cglib.proxy.Enhancer.generateClass(Enhancer.java:447) at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217) at org.mockito.cglib.proxy.Enhancer.createHelper(Enhancer.java:378) at org.mockito.cglib.proxy.Enhancer.createClass(Enhancer.java:318) at org.mockito.internal.creation.jmock.ClassImposterizer.createProxyClass(ClassImposterizer.java:110) at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:62) at org.powermock.api.mockito.internal.mockcreation.MockCreator.createMethodInvocationControl(MockCreator.java:111) at org.powermock.api.mockito.internal.mockcreation.MockCreator.mock(MockCreator.java:60) at org.powermock.api.mockito.PowerMockito.mockStatic(PowerMockito.java:70) at com.test.powermockstatics.TestStaticMockingWithoutPowerMockRunner.testStaticCall(TestStaticMockingWithoutPowerMockRunner.java:30) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:49) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 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)

I am following the recommendation from the docs to:

put powermock-module-junit4-rule-agent before junit in the classpath

Does anyone know the official word if this is a bug in PowerMock or the desired behavior (i.e., you simply can't mock a static method on a final class using a PowerMockRule)?

EDIT:

Please see the clarifying details in the comments under Gábor Lipták's answer. I do not want to use a statically loaded Agent, since it appears the dynamically loaded Agent ought to be capable of getting the job done?

I know starting the agent statically will work. (Unfortunately this is not an option in my project.) So does anyone know if the failure of the dynamically loaded Agent is a bug in PowerMock? Or a known limitation; and why?

Community
  • 1
  • 1
Tom Tresansky
  • 19,364
  • 17
  • 93
  • 129

3 Answers3

15

You need to prepare the class for test!

@PrepareForTest(MyFinalClass.class)

arush436
  • 1,748
  • 20
  • 20
2

For mocking final classes classpath is not enough. You need JVM agent.

According to the docs:

In some cases (such as mocking final classes) it may be necessary to load the PowerMock agent eagerly in Maven in order for the tests to work in Surefire. If you experience this please add the following to your pom.xml:

Needed JVM argument to mock final classes:

-javaagent:${settings.localRepository}/org/powermock/powermock-module-javaagent/1.5.6/powermock-module-javaagent-1.5.6.jar
Gábor Lipták
  • 9,646
  • 2
  • 59
  • 113
  • There is no Maven involved here though. – Tom Tresansky Sep 15 '14 at 20:02
  • Then download the jar, and type command: java .... -javaagent:pathtojar/powermock-module-javaagent-1.5.6.jar . Thats all. – Gábor Lipták Sep 15 '14 at 21:10
  • I agree I need an Agent. Agents can be added dynamically, if you look at the code in https://github.com/jayway/powermock/blob/master/modules/module-impl/agent/src/main/java/org/powermock/modules/agent/PowerMockAgent.java for the initializeIfNeeded method, you'll see it dynamically attaches an Agent to the running JVM process. I unfortunately have to load the Agent dynamically, and cannot add arguments to statically load the agent upon JVM startup. The docs however, make it sound like this (static loading of the agent) is only necessary for use with Maven. – Tom Tresansky Sep 16 '14 at 19:25
  • You are correct that when I toss this argument into my JVM args for my JUnit launch, mocking proceeds correctly. However, I do not know if this (static Agent setup) is a bug or a feature. Dynamically loaded Agents are perfectly capable of transforming already loaded classes (since the FinalClassWithStaticCall class in my example would be loaded prior to the Agent being dynamically loaded) - and the PowerMock agent jar's MANIFEST.MF in fact *DOES* specify Can-Retransform-Classes: true Can-Redefine-Classes: true so It ought to be capable of this. – Tom Tresansky Sep 16 '14 at 19:28
  • Additionally, when tracing through the code to dynamically load the Agent, it appears to be firing the *SAME EXACT* initialize method that static loading calls (PowerMockAgent.java, line 62). So I think my question still stands: is this intended behavior or a bug? It seems to me, after looking at the source, *MORE* likely to be a bug at this point - why isn't the dynamic Agent stripping the final modifiers off my loaded classes if it appears to say it can? I will add these additional clarifying details to the description. Thanks for your help - getting these details right is important! – Tom Tresansky Sep 16 '14 at 19:33
  • Just write an email on Powermock authors, surely they will answer. I do not know Powermock that deep. – Gábor Lipták Sep 16 '14 at 20:58
1

I had the same symptom and could solve it by extending the test class from PowerMockTestCase.

public class NetworkManagerUtilsTest extends PowerMockTestCase {

Not sure if this solution is applicable here.