【问题标题】:Mockito in maven using JPMS cannot access a member of class with modifiers "private"使用JPMS的maven中的Mockito无法访问带有修饰符“private”的类成员
【发布时间】:2021-01-15 03:04:15
【问题描述】:

我正在将代码库迁移到 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 时遇到了类似的问题。

我们将不胜感激。

【问题讨论】:

  • 仅仅基于测试私有的东西没有意义......只测试公共接口......是的模块可以防止这种情况,因为它比私有/受保护/包等更难一般Java 世界

标签: java maven junit mockito java-platform-module-system


【解决方案1】:

TL;DR — 您需要配置 Surefire 插件,以便在运行时将 --add-opens 选项传递给 java测试…

--add-opens

如果您必须允许类路径上的代码执行深度反射以访问非公共成员,请使用--add-opens 运行时选项。

有些库做深度反射,意思是setAccessible(true),所以他们可以访问所有成员,包括私有成员。您可以使用java 命令行上的--add-opens 选项授予此访问权限...


虽然我无法 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。随意修改它,但你认为合适。

【讨论】:

  • 这解决了我的问题,谢谢。令人遗憾的是,它强制为每个项目进行单独的万无一失的配置,但我想这无济于事。
  • 这解决了我的问题,谢谢“——@oscar.haglund——不客气。我为 SO 做出贡献的主要目标是我自己的持续改进。为此,我迫切地想知道:我的答案可以通过哪些方式改进,以便您将其标记为已接受?。或者甚至赞成?您的评论表明我的回答对您有所帮助。那么请您现在回答 my 的问题来帮助 me 吗? Which I've asked before,无济于事。 – “...它强制为每个项目进行单独的万无一失的配置...” - 这是因为每个项目都有不同的需求。 TIA。
【解决方案2】:

另一种解决方案是使用 &lt;useModulePath&gt;false&lt;/useModulePath&gt; 配置 maven-surefire-plugin,这将停止执行模块化访问控制。

注意:这将使您的测试在类路径上运行。通常,您希望测试在尽可能类似于运行时的环境中运行。

【讨论】:

    【解决方案3】:

    我的解决方案是在测试源(Maven 中的src/test/java)中放置一个自己的module-info.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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-23
      • 2015-07-24
      • 2012-08-24
      • 2020-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多