伙计们 问题: 如何模拟一个定义了其他静态最终类的静态最终类?
问题描述: 当我想为最终类 ApplianceRelationshipUtils.java 编写一些单元测试用例时,并且在这个最终类 ApplianceRelationshipUtils.java 中,始终调用另一个静态类 ApplianceUtilities.java。所以我需要使用 powermock 来模拟静态类 ApplianceUtilities.java,如下所示:
// mock the class for one method only
PowerMock.mockStaticPartialNice(ApplianceUtilities.class,"getApplianceVersion");
但是在 ApplianceUtilities.java 中定义了许多静态最终类,如下所示:
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);
所以当我运行测试用例时,会抛出一些异常,如下所示:
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)
我可以确认这些错误来自 Mockito 和 PowerMock 之间的版本不匹配。我们应该仅在父
pom.xml
文件中定义依赖项版本,并在子模块 pom.xml
文件中引用它们。
我在这里提供一个例子。提供了孩子
pom.xml
文件,
<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>
在提供的父
pom.xml
文件中。
<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>
这似乎是 Mockito 和 PowerMock 之间的兼容性问题,所以我在这里留下了对我有用的解决方案,以防其他人遇到同样的错误:
JUnit
JUnit 4.4 或更高版本 如果您愿意,请将以下内容添加到您的 pom.xml 中 使用 JUnit 4.4 或更高版本:
<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 如果您使用 JUnit,请将以下内容添加到您的 pom.xml 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>
使用 Mockito 和 Maven 时的 PowerMock 官方文档:https://github.com/powermock/powermock/wiki/Mockito-Maven
您可以尝试一下@SuppressStaticInitialization,看看是否有帮助
以下内容复制自https://github.com/powermock/powermock/wiki/Suppress-Unwanted-Behavior#suppress-static-initializer
有时,第三方类会在其静态初始化程序(也称为静态构造函数)中执行某些操作,从而阻止您对自己的类进行单元测试。您自己的类也可能在静态初始化程序中执行某些操作,而当您对类进行单元测试时,您不希望发生这种情况。然后,PowerMock 可以简单地抑制该类的静态初始化。您可以通过在测试的类级别或方法级别指定 @SuppressStaticInitializationFor 注释来完成此操作。例如,假设您想要对以下类进行单元测试:
public class ExampleWithEvilStaticInitializer {
static {
System.loadLibrary("evil.dll");
}
private final String message;
public ExampleWithEvilStaticInitializer(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
这里的问题是,当加载ExampleWithEvilStaticInitializer类时,静态代码块将被执行,并且System.loadLibrary(“evil.dll”)将被执行,导致单元测试失败(如果evil.dll无法加载) 。为了抑制这个静态初始化器,我们这样做:
@SuppressStaticInitializationFor("org.mycompany.ExampleWithEvilStaticInitializer")
如您所见,我们没有将ExampleWithEvilStaticInitializer.class传递给@SuppressStaticInitializationFor,而是给它提供了类的完全限定名称。原因是,如果我们将 ExampleWithEvilStaticInitializer.class 传递给注释,静态初始化程序将在测试开始之前运行,因此测试将失败。因此,当删除静态初始值设定项时,您必须将完全限定名称传递给类。整个测试就像:
@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());
}
}
您可以尝试一下@SuppressStaticInitialization,看看是否有帮助。 https://code.google.com/p/powermock/wiki/SuppressUnwantedBehavior