使用 JPMS 的 Maven 中的 Mockito 无法使用修饰符 "private" 访问 class 的成员
Mockito in maven using JPMS cannot access a member of class with modifiers "private"
我正在将代码库迁移到 Java 11 和 JPMS / Jigsaw,但在模拟方面遇到了一些麻烦。
这是我正在尝试的测试 运行。
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class DbTest {
@Mock
private Connection connection;
@Mock
private PreparedStatement preparedStatement;
@Captor
private ArgumentCaptor<Timestamp> dateCaptor;
@Test
public void setTimestamp_instant() throws SQLException {
Instant inputTime = Instant.parse("2018-03-12T10:25:37.386Z");
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
PreparedStatement preparedStatement = connection.prepareStatement("UPDATE fakeTable SET time = ? WHERE TRUE");
RowPack rowPack = new RowPack(preparedStatement, DatabaseType.MYSQL);
rowPack.setTimestamp(inputTime);
verify(preparedStatement).setTimestamp(anyInt(), dateCaptor.capture(), Mockito.any(Calendar.class));
}
}
当 运行 在 Eclipse 中测试时,它通过了,但是当我 运行 它通过 Maven 时,它失败了,因为 mockito 无法使用反射找到一些资源。
org.mockito.exceptions.base.MockitoException: Problems setting field connection annotated with @org.mockito.Mock(name="", stubOnly=false, extraInterfaces={}, answer=RETURNS_DEFAULTS, serializable=false, lenient=false)
Caused by: java.lang.IllegalAccessException: class org.mockito.internal.util.reflection.ReflectionMemberAccessor cannot access a member of class foo.bar.DbTest (in module foo.bar) with modifiers "private"
我正在使用 Surefire 3.0.0-M5、junit 5.7.0 和 mockito 3.5.10。
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
不用说,在切换到使用 JPMS 进行模块化之前,这在 maven 中运行良好。
我已阅读 Testing in the modular world 并尝试使用 junit-platform-maven-plugin 作为 surefire 的替代品,但 运行 遇到与 mockito 类似的问题。
将不胜感激。
TL;DR — 您需要配置 Surefire 插件以将 --add-opens
选项传递给 java
当它运行你的测试时…
--add-opens
If you have to allow code on the class path to do deep reflection to access nonpublic members, then use the --add-opens
runtime option.
Some libraries do deep reflection, meaning setAccessible(true)
, so they can access all members, including private ones. You can grant this access using the --add-opens
option on the java
command line…
虽然我无法 100% 逐字重现您问题中的错误消息,但我能够生成一个几乎相同的错误消息。它与你的非常相似,我相信我和你的根本原因(和解决方案)是相同的。
In this demo that you can download and build,我解决了我遇到的错误...
…
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
…
<argLine>add-opens foo.bar/foo.bar=ALL-UNNAMED</argLine>
…
</configuration>
</plugin>
…
Download and build the demo。您可以随意修改它。
另一种解决方案是使用 <useModulePath>false</useModulePath>
配置 maven-surefire-plugin,这将停止强制执行模块化访问控制。
注意:这将使您的测试 运行 在类路径上。通常,您希望 运行 在与 运行 时尽可能相似的环境中进行测试。
我的解决方案是在测试源中放置一个自己的 module-info.java
(Maven 中的 src/test/java
),为测试模块指定 open
(参见 Allowing runtime-only access to all packages in a module)以下内容:
// "open" seems to be the magic word: it opens up for reflective access
// the same module name like for the main module must be used, so the main module has also the name "com.foo.bar"
open module com.foo.bar {
// I use still juni4
requires junit;
// require Mockito here
requires org.mockito;
// very important, Mockito needs it
requires net.bytebuddy;
// add here your stuff
requires org.bouncycastle.provider;
}
我遇到了类似的问题。我在 Java 11 jpms 项目上使用 Junit 和 Mockito,并希望 运行 使用 maven 进行测试。
为了建立 deduper 的出色答案,我添加了:
<configuration>
<argLine>--add-opens foo.bar/foo.bar=ALL-UNNAMED</argLine>
</configuration>
在我的 surefire 配置上。
注意 2 个带前缀的破折号 --,没有它们 add-opens 被解析为 class 并抛出错误。
我正在将代码库迁移到 Java 11 和 JPMS / Jigsaw,但在模拟方面遇到了一些麻烦。
这是我正在尝试的测试 运行。
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class DbTest {
@Mock
private Connection connection;
@Mock
private PreparedStatement preparedStatement;
@Captor
private ArgumentCaptor<Timestamp> dateCaptor;
@Test
public void setTimestamp_instant() throws SQLException {
Instant inputTime = Instant.parse("2018-03-12T10:25:37.386Z");
when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);
PreparedStatement preparedStatement = connection.prepareStatement("UPDATE fakeTable SET time = ? WHERE TRUE");
RowPack rowPack = new RowPack(preparedStatement, DatabaseType.MYSQL);
rowPack.setTimestamp(inputTime);
verify(preparedStatement).setTimestamp(anyInt(), dateCaptor.capture(), Mockito.any(Calendar.class));
}
}
当 运行 在 Eclipse 中测试时,它通过了,但是当我 运行 它通过 Maven 时,它失败了,因为 mockito 无法使用反射找到一些资源。
org.mockito.exceptions.base.MockitoException: Problems setting field connection annotated with @org.mockito.Mock(name="", stubOnly=false, extraInterfaces={}, answer=RETURNS_DEFAULTS, serializable=false, lenient=false)
Caused by: java.lang.IllegalAccessException: class org.mockito.internal.util.reflection.ReflectionMemberAccessor cannot access a member of class foo.bar.DbTest (in module foo.bar) with modifiers "private"
我正在使用 Surefire 3.0.0-M5、junit 5.7.0 和 mockito 3.5.10。
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
不用说,在切换到使用 JPMS 进行模块化之前,这在 maven 中运行良好。
我已阅读 Testing in the modular world 并尝试使用 junit-platform-maven-plugin 作为 surefire 的替代品,但 运行 遇到与 mockito 类似的问题。
将不胜感激。
TL;DR — 您需要配置 Surefire 插件以将 --add-opens
选项传递给 java
当它运行你的测试时…
--add-opens
If you have to allow code on the class path to do deep reflection to access nonpublic members, then use the
--add-opens
runtime option.Some libraries do deep reflection, meaning
setAccessible(true)
, so they can access all members, including private ones. You can grant this access using the--add-opens
option on thejava
command line…
虽然我无法 100% 逐字重现您问题中的错误消息,但我能够生成一个几乎相同的错误消息。它与你的非常相似,我相信我和你的根本原因(和解决方案)是相同的。
In this demo that you can download and build,我解决了我遇到的错误...
…
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
…
<argLine>add-opens foo.bar/foo.bar=ALL-UNNAMED</argLine>
…
</configuration>
</plugin>
…
Download and build the demo。您可以随意修改它。
另一种解决方案是使用 <useModulePath>false</useModulePath>
配置 maven-surefire-plugin,这将停止强制执行模块化访问控制。
注意:这将使您的测试 运行 在类路径上。通常,您希望 运行 在与 运行 时尽可能相似的环境中进行测试。
我的解决方案是在测试源中放置一个自己的 module-info.java
(Maven 中的 src/test/java
),为测试模块指定 open
(参见 Allowing runtime-only access to all packages in a module)以下内容:
// "open" seems to be the magic word: it opens up for reflective access
// the same module name like for the main module must be used, so the main module has also the name "com.foo.bar"
open module com.foo.bar {
// I use still juni4
requires junit;
// require Mockito here
requires org.mockito;
// very important, Mockito needs it
requires net.bytebuddy;
// add here your stuff
requires org.bouncycastle.provider;
}
我遇到了类似的问题。我在 Java 11 jpms 项目上使用 Junit 和 Mockito,并希望 运行 使用 maven 进行测试。
为了建立 deduper 的出色答案,我添加了:
<configuration>
<argLine>--add-opens foo.bar/foo.bar=ALL-UNNAMED</argLine>
</configuration>
在我的 surefire 配置上。
注意 2 个带前缀的破折号 --,没有它们 add-opens 被解析为 class 并抛出错误。