一直通过 Android Studio 3.6.3/4.0/4.1/4.2配置Robolectric-3.8/4.3.1/4.5.1/4.6.1 Powermock-1.6.6单元测试环境 配置 Powermock 进行单元测试。
虽然配置起来稍显复杂,但是也是够用的。
但是当升级到Android Studio Flamingo | 2022.2.1 Patch 2 之后,内置的 Java 版本被升级到 Java 17 ,使用这个 Java 版本执行单元测试,会报错如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
Failed to instantiate DeepCloner. The DeepCloner implementation must have a one-arg constructor taking a Classloader as parameter. java.lang.RuntimeException: Failed to instantiate DeepCloner. The DeepCloner implementation must have a one-arg constructor taking a Classloader as parameter. at org.powermock.classloading.AbstractClassloaderExecutor.createDeepCloner(AbstractClassloaderExecutor.java:91) at org.powermock.classloading.AbstractClassloaderExecutor.executeWithClassLoader(AbstractClassloaderExecutor.java:55) at org.powermock.classloading.SingleClassloaderExecutor.execute(SingleClassloaderExecutor.java:67) at org.powermock.classloading.AbstractClassloaderExecutor.execute(AbstractClassloaderExecutor.java:43) at org.powermock.modules.junit4.rule.PowerMockStatement.evaluate(PowerMockRule.java:75) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:591) at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:274) at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:88) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:833) Caused by: java.lang.reflect.InvocationTargetException at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) at org.powermock.classloading.AbstractClassloaderExecutor.createDeepCloner(AbstractClassloaderExecutor.java:89) ... 12 more Caused by: java.lang.ExceptionInInitializerError at com.thoughtworks.xstream.XStream.setupConverters(XStream.java:845) at com.thoughtworks.xstream.XStream.<init>(XStream.java:574) at com.thoughtworks.xstream.XStream.<init>(XStream.java:496) at com.thoughtworks.xstream.XStream.<init>(XStream.java:465) at com.thoughtworks.xstream.XStream.<init>(XStream.java:411) at com.thoughtworks.xstream.XStream.<init>(XStream.java:350) at org.powermock.classloading.DeepCloner.<init>(DeepCloner.java:33) ... 18 more Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field protected java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.h accessible: module java.base does not "opens java.lang.reflect" to unnamed module @5a69b2d1 at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354) at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297) at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178) at java.base/java.lang.reflect.Field.setAccessible(Field.java:172) at com.thoughtworks.xstream.core.util.Fields.locate(Fields.java:40) at com.thoughtworks.xstream.converters.extended.DynamicProxyConverter.<clinit>(DynamicProxyConverter.java:42) ... 25 more |
可是 Powermock 已经长时间没有新版本发布,没有及时跟进 Java 版本的更新。
当时引入 Powermock 的原因是为了解决静态函数的测试问题,但是从 Mockito 3.4.0 版本开始,Mockito 已经支持静态函数测试。
因此,完全可以只使用 Mockto 进行测试。
1 2 |
// https://mvnrepository.com/artifact/org.mockito/mockito-core testImplementation 'org.mockito:mockito-core:5.3.1' |
待测试代码如下:
1 2 3 4 5 6 7 8 9 10 |
public class StaticUtils { public static String name() { return "name"; } public static String getString(String s) { return s; } } |
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import org.junit.Assert; import org.junit.Test; import org.mockito.ArgumentMatchers; import org.mockito.MockedStatic; import org.mockito.Mockito; public class StaticUtilsTest { @Test public void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() { Assert.assertEquals(StaticUtils.name(), "name"); try (MockedStatic<StaticUtils> utils = Mockito.mockStatic(StaticUtils.class)) { utils.when(StaticUtils::name).thenReturn("uname"); Assert.assertEquals(StaticUtils.name(), "uname"); utils.when(() -> StaticUtils.getString(ArgumentMatchers.anyString())).thenReturn("target"); Assert.assertEquals(StaticUtils.getString("123"), "target"); } Assert.assertEquals(StaticUtils.name(), "name"); Assert.assertEquals(StaticUtils.getString("123"), "123"); } } |
参考链接
- Android Studio 3.6.3/4.0/4.1/4.2配置Robolectric-3.8/4.3.1/4.5.1/4.6.1 Powermock-1.6.6单元测试环境
- Mocking Static Methods With Mockito
- Kotlin 写 Android 单元测试(三),Mockito mocking 框架的使用
- 使用Mockito模拟Static静态方法
- 使用mockito来mock final、static、private以及构造方法
- Mock Java Constructors With Mockito | Configuration and Examples