TestNG 测试针对 JDK 9 模块的执行导致 InaccessibleObjectException
TestNG tests execution against JDK 9 module causes InaccessibleObjectException
我正在尝试将以下库转换为 Java 9 模块:https://github.com/sskorol/test-data-supplier
已遵循本指南:https://guides.gradle.org/building-java-9-modules
经过一些操作和重构(无法处理 lombok 问题,所以只是暂时删除它),我有以下 module-info.java:
module io.github.sskorol {
exports io.github.sskorol.core;
exports io.github.sskorol.model;
requires testng;
requires vavr;
requires streamex;
requires joor;
requires aspectjrt;
}
它甚至可以在测试跳过的情况下进行编译/构建。
但是,当我尝试 运行 一个 test 任务时,出现以下异常:
org.gradle.api.internal.tasks.testing.TestSuiteExecutionException: Could not complete execution for Gradle Test Executor 2.
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy1.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:120)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl.run(ManagedExecutorImpl.java:46)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: org.testng.TestNGException:
Cannot instantiate class io.github.sskorol.testcases.DataSupplierTests
at testng@6.11/org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:31)
at testng@6.11/org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:410)
at testng@6.11/org.testng.internal.ClassHelper.createInstance(ClassHelper.java:323)
at testng@6.11/org.testng.internal.ClassImpl.getDefaultInstance(ClassImpl.java:126)
at testng@6.11/org.testng.internal.ClassImpl.getInstances(ClassImpl.java:191)
at testng@6.11/org.testng.TestClass.getInstances(TestClass.java:99)
at testng@6.11/org.testng.TestClass.initTestClassesAndInstances(TestClass.java:85)
at testng@6.11/org.testng.TestClass.init(TestClass.java:77)
at testng@6.11/org.testng.TestClass.<init>(TestClass.java:42)
at testng@6.11/org.testng.TestRunner.initMethods(TestRunner.java:423)
at testng@6.11/org.testng.TestRunner.init(TestRunner.java:250)
at testng@6.11/org.testng.TestRunner.init(TestRunner.java:220)
at testng@6.11/org.testng.TestRunner.<init>(TestRunner.java:161)
at testng@6.11/org.testng.SuiteRunner$DefaultTestRunnerFactory.newTestRunner(SuiteRunner.java:578)
at testng@6.11/org.testng.SuiteRunner.init(SuiteRunner.java:185)
at testng@6.11/org.testng.SuiteRunner.<init>(SuiteRunner.java:131)
at testng@6.11/org.testng.TestNG.createSuiteRunner(TestNG.java:1383)
at testng@6.11/org.testng.TestNG.createSuiteRunners(TestNG.java:1363)
at testng@6.11/org.testng.TestNG.runSuitesLocally(TestNG.java:1217)
at testng@6.11/org.testng.TestNG.runSuites(TestNG.java:1144)
at testng@6.11/org.testng.TestNG.run(TestNG.java:1115)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.runTests(TestNGTestClassProcessor.java:129)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.stop(TestNGTestClassProcessor.java:88)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
... 25 more
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make public io.github.sskorol.testcases.DataSupplierTests() accessible: module io.github.sskorol does not "exports io.github.sskorol.testcases" to module testng
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
at java.base/java.lang.reflect.Constructor.checkCanSetAccessible(Constructor.java:192)
at java.base/java.lang.reflect.Constructor.setAccessible(Constructor.java:185)
at testng@6.11/org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:22)
... 48 more
我觉得有点困惑,因为 io.github.sskorol.testcases 是 src/test/java 的一部分,并且有没有 module-info 用于测试。所以我无法将这个包导出到 TestNG。假设 ObjectFactoryImpl 中的 TestNG 反射使用的根本原因是针对测试 类.
有人知道如何解决吗?
环境:JDK9(build 9+181),Gradle4.1,TestNG 6.11
Have an assumption that the root cause in a TestNG reflection usage within ObjectFactoryImpl
against test classes.
是的,这是两个原因之一。另一个是,显然 Gradle 将您的测试作为一个模块运行。正如您所指出的,您的测试没有模块描述符。 Gradle 可以使用 --patch-module
将测试添加到包含生产代码的模块中。
提供了大量背景信息和可能的修复。作为短期修复,我建议将 opens io.github.sskorol.testcases
添加到生产代码的模块描述符中。从它的名字来看,我猜还没有这样的包,所以你必须重命名或添加一个虚拟 class (我更喜欢前者)。
我还会将此问题提交给 Gradle 邮件列表或错误跟踪器。除非我们忽略了一些东西(完全有可能),否则 Gradle 的行为是非常不幸的,因为它需要调整生产代码的模块描述符以适应测试代码的需要。
如果测试与被测模块在同一个包中,则需要编译它们(使用 --patch-module
)以便它们被编译 "as if" 它们是模块的一部分。测试中对 TestNG 类型的引用意味着 --add-reads io.github.sskorol=testng
也将是必需的。
运行 类似。测试需要 运行 "as if" 它们在模块 io.github.sskorol
中。这意味着 运行:
--patch-module io.github.sskorol=<testclasses> \
--add-reads io.github.sskorol=testng
此外,您可能需要将包含测试的包导出或打开到 TestNG。这将归结为测试 类 和方法是否在导出包中 public。为了避免扫描,最简单的是将所有包含测试的包打开到 TestNG,例如
--add-opens io.github.sskorol/io.github.sskorol.core=testng \
--add-opens io.github.sskorol/io.github.sskorol.core.internal=testng
(.internal
只是模块中非导出包的填充)
所有这些看起来可能很复杂,但这是 Maven Surefire 插件和 Gradle 应该做的事情。
做了@AlanBateman 建议的一些技巧,他给了我一个有效的方向。
最后想出了如下配置:
模块-info.java
module io.github.sskorol {
exports io.github.sskorol.core;
exports io.github.sskorol.model;
opens io.github.sskorol.utils to joor;
requires testng;
requires vavr;
requires streamex;
requires joor;
}
build.gradle
test {
inputs.property("moduleName", moduleName)
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'ALL-MODULE-PATH',
'--add-opens', 'io.github.sskorol/io.github.sskorol.testcases=testng',
'--add-opens', 'io.github.sskorol/io.github.sskorol.testcases=joor',
'--add-opens', 'io.github.sskorol/io.github.sskorol.datasuppliers=joor',
'--add-opens', 'io.github.sskorol/io.github.sskorol.datasuppliers=testng',
'--add-opens', 'java.base/java.util=streamex',
'--add-opens', 'java.base/java.util.stream=streamex',
'--patch-module', "$moduleName=" + files(sourceSets.test.java.outputDir).asPath
]
classpath = files()
}
}
testng
和 joor
都需要访问我的测试包。所以 --add-opens
标志成功了。 streamex
模块还担心访问 java.base
个包。
请注意,与原始 Java 8 代码相比,我不得不删除 lombok 和 aspectj 依赖项,因为我不能'设法完全解决迁移后出现的所有问题。
我遇到的另一个问题与 SPI 测试有关。根据我读过的文档,在 Java 中,9 个 SPI 实现应该列在 module-info.java
而不是 META-INF/services
中。但它似乎不是一个解决方案,当实现 class 位于 test
包之一时,再次 test
不是一个模块。只是想知道是否有一些 jvmflag 用于替换 provides ... with ...
语法,并使用与 --add-opens
相同的技巧。如有任何想法,我们将不胜感激。
完全实现,修改为支持Java9,可以找到here。
我正在尝试将以下库转换为 Java 9 模块:https://github.com/sskorol/test-data-supplier
已遵循本指南:https://guides.gradle.org/building-java-9-modules
经过一些操作和重构(无法处理 lombok 问题,所以只是暂时删除它),我有以下 module-info.java:
module io.github.sskorol {
exports io.github.sskorol.core;
exports io.github.sskorol.model;
requires testng;
requires vavr;
requires streamex;
requires joor;
requires aspectjrt;
}
它甚至可以在测试跳过的情况下进行编译/构建。 但是,当我尝试 运行 一个 test 任务时,出现以下异常:
org.gradle.api.internal.tasks.testing.TestSuiteExecutionException: Could not complete execution for Gradle Test Executor 2.
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:63)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy1.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:120)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
at org.gradle.internal.concurrent.ManagedExecutorImpl.run(ManagedExecutorImpl.java:46)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
at java.base/java.lang.Thread.run(Thread.java:844)
Caused by: org.testng.TestNGException:
Cannot instantiate class io.github.sskorol.testcases.DataSupplierTests
at testng@6.11/org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:31)
at testng@6.11/org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:410)
at testng@6.11/org.testng.internal.ClassHelper.createInstance(ClassHelper.java:323)
at testng@6.11/org.testng.internal.ClassImpl.getDefaultInstance(ClassImpl.java:126)
at testng@6.11/org.testng.internal.ClassImpl.getInstances(ClassImpl.java:191)
at testng@6.11/org.testng.TestClass.getInstances(TestClass.java:99)
at testng@6.11/org.testng.TestClass.initTestClassesAndInstances(TestClass.java:85)
at testng@6.11/org.testng.TestClass.init(TestClass.java:77)
at testng@6.11/org.testng.TestClass.<init>(TestClass.java:42)
at testng@6.11/org.testng.TestRunner.initMethods(TestRunner.java:423)
at testng@6.11/org.testng.TestRunner.init(TestRunner.java:250)
at testng@6.11/org.testng.TestRunner.init(TestRunner.java:220)
at testng@6.11/org.testng.TestRunner.<init>(TestRunner.java:161)
at testng@6.11/org.testng.SuiteRunner$DefaultTestRunnerFactory.newTestRunner(SuiteRunner.java:578)
at testng@6.11/org.testng.SuiteRunner.init(SuiteRunner.java:185)
at testng@6.11/org.testng.SuiteRunner.<init>(SuiteRunner.java:131)
at testng@6.11/org.testng.TestNG.createSuiteRunner(TestNG.java:1383)
at testng@6.11/org.testng.TestNG.createSuiteRunners(TestNG.java:1363)
at testng@6.11/org.testng.TestNG.runSuitesLocally(TestNG.java:1217)
at testng@6.11/org.testng.TestNG.runSuites(TestNG.java:1144)
at testng@6.11/org.testng.TestNG.run(TestNG.java:1115)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.runTests(TestNGTestClassProcessor.java:129)
at org.gradle.api.internal.tasks.testing.testng.TestNGTestClassProcessor.stop(TestNGTestClassProcessor.java:88)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
... 25 more
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make public io.github.sskorol.testcases.DataSupplierTests() accessible: module io.github.sskorol does not "exports io.github.sskorol.testcases" to module testng
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:337)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:281)
at java.base/java.lang.reflect.Constructor.checkCanSetAccessible(Constructor.java:192)
at java.base/java.lang.reflect.Constructor.setAccessible(Constructor.java:185)
at testng@6.11/org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:22)
... 48 more
我觉得有点困惑,因为 io.github.sskorol.testcases 是 src/test/java 的一部分,并且有没有 module-info 用于测试。所以我无法将这个包导出到 TestNG。假设 ObjectFactoryImpl 中的 TestNG 反射使用的根本原因是针对测试 类.
有人知道如何解决吗?
环境:JDK9(build 9+181),Gradle4.1,TestNG 6.11
Have an assumption that the root cause in a TestNG reflection usage within
ObjectFactoryImpl
against test classes.
是的,这是两个原因之一。另一个是,显然 Gradle 将您的测试作为一个模块运行。正如您所指出的,您的测试没有模块描述符。 Gradle 可以使用 --patch-module
将测试添加到包含生产代码的模块中。
opens io.github.sskorol.testcases
添加到生产代码的模块描述符中。从它的名字来看,我猜还没有这样的包,所以你必须重命名或添加一个虚拟 class (我更喜欢前者)。
我还会将此问题提交给 Gradle 邮件列表或错误跟踪器。除非我们忽略了一些东西(完全有可能),否则 Gradle 的行为是非常不幸的,因为它需要调整生产代码的模块描述符以适应测试代码的需要。
如果测试与被测模块在同一个包中,则需要编译它们(使用 --patch-module
)以便它们被编译 "as if" 它们是模块的一部分。测试中对 TestNG 类型的引用意味着 --add-reads io.github.sskorol=testng
也将是必需的。
运行 类似。测试需要 运行 "as if" 它们在模块 io.github.sskorol
中。这意味着 运行:
--patch-module io.github.sskorol=<testclasses> \
--add-reads io.github.sskorol=testng
此外,您可能需要将包含测试的包导出或打开到 TestNG。这将归结为测试 类 和方法是否在导出包中 public。为了避免扫描,最简单的是将所有包含测试的包打开到 TestNG,例如
--add-opens io.github.sskorol/io.github.sskorol.core=testng \
--add-opens io.github.sskorol/io.github.sskorol.core.internal=testng
(.internal
只是模块中非导出包的填充)
所有这些看起来可能很复杂,但这是 Maven Surefire 插件和 Gradle 应该做的事情。
做了@AlanBateman 建议的一些技巧,他给了我一个有效的方向。
最后想出了如下配置:
模块-info.java
module io.github.sskorol {
exports io.github.sskorol.core;
exports io.github.sskorol.model;
opens io.github.sskorol.utils to joor;
requires testng;
requires vavr;
requires streamex;
requires joor;
}
build.gradle
test {
inputs.property("moduleName", moduleName)
doFirst {
jvmArgs = [
'--module-path', classpath.asPath,
'--add-modules', 'ALL-MODULE-PATH',
'--add-opens', 'io.github.sskorol/io.github.sskorol.testcases=testng',
'--add-opens', 'io.github.sskorol/io.github.sskorol.testcases=joor',
'--add-opens', 'io.github.sskorol/io.github.sskorol.datasuppliers=joor',
'--add-opens', 'io.github.sskorol/io.github.sskorol.datasuppliers=testng',
'--add-opens', 'java.base/java.util=streamex',
'--add-opens', 'java.base/java.util.stream=streamex',
'--patch-module', "$moduleName=" + files(sourceSets.test.java.outputDir).asPath
]
classpath = files()
}
}
testng
和 joor
都需要访问我的测试包。所以 --add-opens
标志成功了。 streamex
模块还担心访问 java.base
个包。
请注意,与原始 Java 8 代码相比,我不得不删除 lombok 和 aspectj 依赖项,因为我不能'设法完全解决迁移后出现的所有问题。
我遇到的另一个问题与 SPI 测试有关。根据我读过的文档,在 Java 中,9 个 SPI 实现应该列在 module-info.java
而不是 META-INF/services
中。但它似乎不是一个解决方案,当实现 class 位于 test
包之一时,再次 test
不是一个模块。只是想知道是否有一些 jvmflag 用于替换 provides ... with ...
语法,并使用与 --add-opens
相同的技巧。如有任何想法,我们将不胜感激。
完全实现,修改为支持Java9,可以找到here。