Reputation: 41
Guys Question: How to mock a static final class which has defined some other static final class?
Problem Description: When I want to write some unit test case for a final class ApplianceRelationshipUtils.java, and in this final class ApplianceRelationshipUtils.java, always invork another static class ApplianceUtilities.java. So I need to use powermock to mock the static class ApplianceUtilities.java like this:
// mock the class for one method only
PowerMock.mockStaticPartialNice(ApplianceUtilities.class,"getApplianceVersion");
But in the ApplianceUtilities.java defined many static final classs like these:
private static final IMesUtils m_mesUtils = new MesUtils();
private static final IEndpointUtilities m_endpointUtils = new DefaultEndpointUtilities(m_mesUtils);
private static final ICasUtilities m_casUtils = new DefaultCasUtilities(m_endpointUtils);
private static final IHAApplianceUtilities m_haUtils = new HAApplianceUtils(m_endpointUtils, m_casUtils);
So when I run the testcase there will throw out some exception as below:
java.lang.NoSuchMethodError: com.ibm.usmi.services.updates.util.RestartUtilities.<init>(Lcom/ibm/usmi/services/updates/util/IRcsUtilities;Lcom/ibm/usmi/services/updates/util/MesUtils;Lcom/ibm/usmi/services/updates/util/IUpdateUtils;)V
at com.ibm.vmi.updates.appliance.util.HAApplianceUtils.<init>(HAApplianceUtils.java:222)
at com.ibm.vmi.updates.appliance.util.ApplianceUtilities.<clinit>(ApplianceUtilities.java:89)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.sf.cglib.proxy.Enhancer.setCallbacksHelper(Enhancer.java:616)
at net.sf.cglib.proxy.Enhancer.setThreadCallbacks(Enhancer.java:609)
at net.sf.cglib.proxy.Enhancer.registerCallbacks(Enhancer.java:578)
at org.easymock.internal.ClassProxyFactory.createProxy(ClassProxyFactory.java:194)
at org.easymock.internal.MocksControl.createMock(MocksControl.java:60)
at org.easymock.internal.MocksControl.createMock(MocksControl.java:98)
at org.powermock.api.easymock.PowerMock.doCreateMock(PowerMock.java:2214)
at org.powermock.api.easymock.PowerMock.doMock(PowerMock.java:2163)
at org.powermock.api.easymock.PowerMock.createMock(PowerMock.java:76)
at org.powermock.api.easymock.PowerMock.createPartialMock(PowerMock.java:762)
at com.ibm.vmi.updates.appliance.relationship.ApplianceRelationshipUtilsTest.test_validateApplianceReqs(ApplianceRelationshipUtilsTest.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:312)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:296)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:112)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:73)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:284)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:209)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:42)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49)
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)
Upvotes: 4
Views: 11721
Reputation: 473
You can give @SuppressStaticInitializationFor a try and see if that helps
The below is copied from https://github.com/powermock/powermock/wiki/Suppress-Unwanted-Behavior#suppress-static-initializer
Some times a thrid-party class does something in its static initializer (also called static constructor) that prevents you from unit testing your own class. It's also possible that your own class does something in a static initializer which you don't want to happen when you unit test your class. PowerMock can then simply suppress the static initialization of that class. You do this by specifying the @SuppressStaticInitializationFor annotation at the class-level or method-level of the test. For example let's say you want to unit-test the following class:
public class ExampleWithEvilStaticInitializer {
static {
System.loadLibrary("evil.dll");
}
private final String message;
public ExampleWithEvilStaticInitializer(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
The problem here is that when the ExampleWithEvilStaticInitializer class is loaded the static code block will be executed and the System.loadLibrary("evil.dll") will be executed causing the unit test to fail (if the evil.dll cannot be loaded). To suppress this static initializer we do like this:
@SuppressStaticInitializationFor("org.mycompany.ExampleWithEvilStaticInitializer")
As you can see we don't pass the ExampleWithEvilStaticInitializer.class to the @SuppressStaticInitializationFor but instead we give it the fully-qualified name of the class instead. The reason is that if we were to pass ExampleWithEvilStaticInitializer.class to the annotation the static initializer would run before the test has even started and thus the test would fail. So when removing a static initializer you have to pass the fully-qualified name to the class instead. The whole test would like like:
@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("org.mycompany.ExampleWithEvilStaticInitializer")
public class ExampleWithEvilStaticInitializerTest {
@Test
public void testSuppressStaticInitializer() throws Exception {
final String message = "myMessage";
ExampleWithEvilStaticInitializer tested = new ExampleWithEvilStaticInitializer(message);
assertEquals(message, tested.getMessage());
}
}
Upvotes: 0
Reputation: 12451
I can confirm those errors are coming from the versions mismatch between Mockito and PowerMock. We should define the dependency versions only in the parent pom.xml
file and reference them in the child module pom.xml
files.
I provide an example here. The child pom.xml
file provided,
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
In the parent pom.xml
file provided.
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
...........................................................
<properties>
<powermock.version>1.7.4</powermock.version>
<powermock-api.version>2.0.2</powermock-api.version>
<mockito.version>2.8.9</mockito.version>
</properties>
.............................
</project>
Upvotes: 1
Reputation: 1427
This seems to be a compatibility issue between Mockito and PowerMock so I leave here the solution that worked for me in case someone else runs into the same error:
JUnit
JUnit 4.4 or above Add the following to your pom.xml if you're using JUnit 4.4 or above:
<properties> <powermock.version>1.7.1</powermock.version> </properties> <dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency>
JUnit 4.0-4.3 Add the following to your pom.xml if you're using JUnit 4.0-4.3:
<properties> <powermock.version>1.7.1</powermock.version> </properties> <dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4-legacy</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>${powermock.version}</version> <scope>test</scope> </dependency> </dependencies>
Official documentation for PowerMock when using Mockito and Maven: https://github.com/powermock/powermock/wiki/Mockito-Maven
Upvotes: 0
Reputation: 1139
You can give @SuppressStaticInitializationFor a try and see if that helps. https://code.google.com/p/powermock/wiki/SuppressUnwantedBehavior
Upvotes: -1