Mockito不能在Java17中模拟Random



试图将我的项目从Java 11更新到Java 17,但在特定测试中从Mockito收到了一个意外错误。

mock(java.util.Random.class);

投掷

Feb 04, 2022 3:07:01 PM com.google.inject.internal.MessageProcessor visit
INFO: An exception was caught and reported. Message: java.lang.IllegalAccessException: class 
net.bytebuddy.description.annotation.AnnotationDescription$ForLoadedAnnotation cannot access interface
jdk.internal.util.random.RandomSupport$RandomGeneratorProperties (in module java.base) 
because module java.base does not export jdk.internal.util.random to unnamed module @2f54a33d
org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class java.util.Random.
Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.

Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17.0.2+8-86
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17.0.2+8-86
JVM info           : mixed mode, sharing
OS name            : Mac OS X
OS version         : 12.1

不确定Mockito为什么在这次测试中失败。

这个特殊问题也可以使用解决

mock(SecureRandom.class, withSettings().withoutAnnotations())

这里的问题是mockito(通过ByteBuddy(试图在运行时使用不可访问的类型(通过反射(。从Java 9开始,除非显式导出/打开模块,否则并非所有模块都可以访问。

由于这是一个运行时问题,您可以将--add-opens添加为JVM arg/CLI选项,以使此类型可访问。

根据此处的Oracle指南,--add-opens执行以下操作。

如果必须允许类路径上的代码对访问非公共成员,然后使用--add-opens运行时选项。

如果您也想导出编译时可用的内部类型,可以使用--add-exports

解决您的具体问题;使用以下内容。

--add-opens java.base/jdk.internal.util.random=ALL-UNNAMED

ALL-UNNAMED意味着,指定的包在整个代码库中是可用的。

然而,嘲笑那些不属于你的类型并不是一个好的做法。也许,如果有其他选择,你可以简化它。

将mokito升级到4.4.0,它工作于

需要mock Random表示您的代码还没有被编写成可测试的。这样做:

interface RandomSource {
/**
* Return a random number that matches the criteria you need
*/
int nextNumber();
}
@Bean
class DefaultRandomSource implements RandomSource {
private final Random r = new Random();
public int nextNumber() {
return r.nextInt(2);
}
}

@Bean
class ClassToTest {
private final RandomSource randomSource;
@Autowired
public ClassToTest(RandomSource randomSource) {
this.randomSource = randomSource;
}
public String doSomething() {
return randomSource.nextNumber() == 0 ? "Foo" : "Bar";
}
}

@Test
void testDoSomething() {
RandomSource r = mock(RandomSource.class);
when(r.nextNumber()).thenReturn(0);
ClassToTest classToTest = new ClassToTest(r);
assertEquals("Foo", classToTest.doSomething());
}

最新更新