Android Studio Flamingo | 2022.2.1 Patch 2 配置Robolectric-3.8/4.3.1/4.5.1/4.6.1单元测试环境

一直通过 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 版本执行单元测试,会报错如下:

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 进行测试。

// https://mvnrepository.com/artifact/org.mockito/mockito-core
testImplementation 'org.mockito:mockito-core:5.3.1'

待测试代码如下:

public class StaticUtils {

    public static String name() {
        return "name";
    }

    public static String getString(String s) {
        return s;
    }
}

测试代码如下:

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");
    }
}

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注