Using PowerMock in JDK 16



Java反射越来越受到限制:

  • 在Java 8之前,允许所有操作
  • 从Java 9到15,您仍然可以执行操作,但会收到警告
  • 从Java 16及以后,模块之间的操作是被禁止的(好吧,通过将一些特殊的参数传递给JVM,这仍然是可能的(

当使用严重依赖反射的库时,这是一个严重的问题,比如PowerMock在测试中使用它来模拟对象。

我创建了一个简单的例子来说明这个问题。这是pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Powermock</name>
<properties>
<java.version>16</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
</plugins>
</build>
</project>

有了这个简单的测试SomeTest.java:

package com.example.demo;
import org.junit.Assert;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
class SomeTest {
@Test
void contextLoads() {
Assert.assertEquals(1, 1);
}
}

然后,如果我们运行命令mvn clean test,我们将得到错误:

Running com.example.demo.SomeTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.341 sec <<< FAILURE!
initializationError(com.example.demo.SomeTest)  Time elapsed: 0 sec  <<< ERROR!
java.lang.RuntimeException: java.lang.reflect.InaccessibleObjectException: Unable to make protected native
java.lang.Object java.lang.Object.clone() throws java.lang.CloneNotSupportedException accessible:
module java.base does not "opens java.lang" to unnamed module @8b96fde

奇怪的是,如果我们将这个比特添加到pom.xml中,问题就会消失:

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
</parent>

任何能帮我的人:

  • 如何在JDK16中使用PowerMock
  • JDK 16甚至支持PowerMock吗
  • 这个问题是使用Java模块解决的吗
  • 为什么添加Spring Boot作为父级可以解决问题

在POM文件中使用此插件:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/java.base=ALL-UNNAMED
</argLine>
</configuration>
</plugin>

对于Java 17和更高版本,我在我的POM中使用它(测试到Java 20(:

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<!--suppress MavenModelInspection -->
<configuration>
<argLine>
${argLine}
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens
java.base/java.util.stream=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens
java.base/java.util.regex=ALL-UNNAMED
--add-opens
java.base/java.nio.channels.spi=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens
java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/sun.nio.fs=ALL-UNNAMED
--add-opens java.base/sun.nio.cs=ALL-UNNAMED
--add-opens java.base/java.nio.file=ALL-UNNAMED
--add-opens
java.base/java.nio.charset=ALL-UNNAMED
--add-opens
java.base/java.lang.reflect=ALL-UNNAMED
--add-opens
java.logging/java.util.logging=ALL-UNNAMED
--add-opens java.base/java.lang.ref=ALL-UNNAMED
--add-opens java.base/java.util.jar=ALL-UNNAMED
--add-opens java.base/java.util.zip=ALL-UNNAMED
</argLine>
</configuration>
</plugin>

此外,如果您正在使用Eclipse并希望在IDE中运行JUnit测试,那么除非您告诉它上面的--add-opens,否则它将失败。要执行此操作,请转到"首选项"->Java->已安装的JRE,然后选择活动的JRE/JDK,然后单击"编辑"。然后在"默认VM参数"框中复制并粘贴所有--add-opens(不包括{$argLine}引用(。一旦您完成了这项工作,您就应该能够在没有任何错误的情况下运行JUnit测试。

仅限Java 16的旧答案:通过使用Maven surefire插件中的--illegal-access=permit选项,您可以将PowerMockito与Java 16一起使用。你会看到警告,但它是有效的。

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<argLine>
--illegal-access=permit
</argLine>
</configuration>
</plugin>

作为参考,这是我用于Mockito和PowerMockito的内容:

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.11.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>

最新更新