针对JDK 9模块的TestNG测试执行导致InaccessibleObjectException

时间:2017-08-14 11:25:16

标签: java gradle testng java-9

我试图将以下库转换为Java 9模块:https://github.com/sskorol/test-data-supplier

遵循本指南:https://guides.gradle.org/building-java-9-modules

经过一些操作和重构(无法管理 lombok 问题,所以只是临时删除它),我有以下 module-info.java :< / p>

module io.github.sskorol {
    exports io.github.sskorol.core;
    exports io.github.sskorol.model;

    requires testng;
    requires vavr;
    requires streamex;
    requires joor;
    requires aspectjrt;
}

它甚至可以在测试的情况下编译/构建&#39;跳绳。 但是,当我尝试运行 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$1.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 的一部分而且 < em> module-info 用于测试。所以我无法将此软件包导出到TestNG。假设 ObjectFactoryImpl 中的TestNG反射用法对测试类的根本原因。

有没有人知道如何解决它?

环境:JDK 9(版本9 + 181),Gradle 4.1,TestNG 6.11

3 个答案:

答案 0 :(得分:6)

  

假设ObjectFactoryImpl内的TestNG反射用法对测试类的根本原因。

这是两个原因之一,是的。另一个是,显然,Gradle将您的测试作为一个模块运行。正如您所指出的那样,您的测试没有模块描述符。 Gradle可以使用--patch-module将测试添加到包含生产代码的模块中。

This question and answer提供了大量背景信息和可能的修复方法。作为短期修复,我建议将opens io.github.sskorol.testcases添加到您的生产代码的模块描述符中。从它的名字来看,我猜测还没有这样的包,所以你要么必须重命名或添加一个虚拟类(我更喜欢前者)。

我也会将此问题发送到Gradle邮件列表或错误跟踪器。除非我们忽略了某些事情(完全可能),否则Gradle的行为非常不幸,因为它需要根据测试代码的需要调整生产代码的模块描述符。

答案 1 :(得分:1)

如果测试与被测模块位于同一个软件包中,则需要对它们进行编译(使用--patch-module),以便对它们进行编译,如同&#34;它们是模块的一部分。测试中对TestNG类型的引用意味着也需要--add-reads io.github.sskorol=testng

跑步很相似。测试需要运行&#34;好像&#34;它们在模块io.github.sskorol中。这意味着运行:

--patch-module io.github.sskorol=<testclasses> \
--add-reads io.github.sskorol=testng

此外,您可能需要将包含测试的软件包导出或打开到TestNG。这将归结为测试类和方法在导出的包中是否公开。为了避免扫描,最简单的方法是打开包含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应该做的事情。

答案 2 :(得分:0)

@AlanBateman提出了一些技巧,他给了我一个有效的指示。

最后,我想出了以下配置:

<强> module-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()
    }
}

testngjoor都需要访问我的测试包。所以--add-opens标志就行了。 streamex模块也关注访问java.base个包。

请注意,与原始Java 8代码相比,我必须删除 lombok aspectj 依赖项,因为我无法完全解决迁移后发生的所有问题

我遇到的另一个问题与SPI测试有关。根据我读过的文档,在Java 9中,SPI实现应该列在module-info.java而不是META-INF/services中。但是,当实现类位于test个包之一时,它似乎不是一个解决方案,因为test不是一个模块。只是想知道是否有一些jvmflag用于替换provides ... with ...语法,并且使用与--add-opens相同的技巧。任何想法都将不胜感激。

可以找到已修改为支持Java 9的完整实现here