Mockito 无法在 Java 17 中模拟 Random
Mockito can not mock Random in Java 17
尝试将我的项目从 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 在此测试中失败。
这里的问题是 mockito(通过 ByteBuddy)试图在运行时(通过反射)使用不可访问的类型。从 Java 9 开始,并非所有模块都可以访问,除非您明确 export/open 它们。
由于这是一个运行时问题,您可以添加 --add-opens
作为 JVM arg/CLI 选项以使该类型可访问。
根据 Oracle 指南 here,--add-opens
执行以下操作。
If you have to allow code on the classpath to do deep reflection to
access nonpublic members, then use the --add-opens runtime option.
如果你想导出编译时可用的内部类型,你可以使用--add-exports
.
解决您的具体问题;使用以下内容。
--add-opens java.base/jdk.internal.util.random=ALL-UNNAMED
.
ALL-UNNAMED
表示指定的包在整个代码库中可用。
但是,模拟不属于您的类型不是一个好习惯。也许,如果有其他选择,您可以简化它。
需要模拟 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());
}
这个特殊问题也可以通过以下方式解决:
mock(SecureRandom.class, withSettings().withoutAnnotations())
将 mokito 升级到 4.4.0 有效
尝试将我的项目从 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 在此测试中失败。
这里的问题是 mockito(通过 ByteBuddy)试图在运行时(通过反射)使用不可访问的类型。从 Java 9 开始,并非所有模块都可以访问,除非您明确 export/open 它们。
由于这是一个运行时问题,您可以添加 --add-opens
作为 JVM arg/CLI 选项以使该类型可访问。
根据 Oracle 指南 here,--add-opens
执行以下操作。
If you have to allow code on the classpath to do deep reflection to access nonpublic members, then use the --add-opens runtime option.
如果你想导出编译时可用的内部类型,你可以使用--add-exports
.
解决您的具体问题;使用以下内容。
--add-opens java.base/jdk.internal.util.random=ALL-UNNAMED
.
ALL-UNNAMED
表示指定的包在整个代码库中可用。
但是,模拟不属于您的类型不是一个好习惯。也许,如果有其他选择,您可以简化它。
需要模拟 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());
}
这个特殊问题也可以通过以下方式解决:
mock(SecureRandom.class, withSettings().withoutAnnotations())
将 mokito 升级到 4.4.0 有效