使用JPMS的maven中的Mockito无法访问带有修饰符“ private”的类的成员 --add-opens

问题描述

我正在将代码库迁移到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

如果必须允许类路径上的代码进行深层反思来访问非公共成员,请使用--add-opens运行时选项。

某些库进行深层反射,表示setAccessible(true),因此它们可以访问所有成员,包括私有成员。您可以使用--add-opens命令行上的java选项授予此访问权限...


尽管我无法完全按照您的问题中的错误消息来重现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。随意修改它,但您认为合适。