Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TestNG tests execution against JDK 9 module causes InaccessibleObjectException

I'm trying to transform the following library into Java 9 module: https://github.com/sskorol/test-data-supplier

Followed this guide: https://guides.gradle.org/building-java-9-modules

After some manipulations and refactoring (couldn't manage lombok issues, so just temporary removed it), I have the following 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;
}

And it even compiles / builds in case of tests' skipping. However, when I try to run a test task, I'm getting the following exception:

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 [email protected]/org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:31)
    at [email protected]/org.testng.internal.ClassHelper.createInstance1(ClassHelper.java:410)
    at [email protected]/org.testng.internal.ClassHelper.createInstance(ClassHelper.java:323)
    at [email protected]/org.testng.internal.ClassImpl.getDefaultInstance(ClassImpl.java:126)
    at [email protected]/org.testng.internal.ClassImpl.getInstances(ClassImpl.java:191)
    at [email protected]/org.testng.TestClass.getInstances(TestClass.java:99)
    at [email protected]/org.testng.TestClass.initTestClassesAndInstances(TestClass.java:85)
    at [email protected]/org.testng.TestClass.init(TestClass.java:77)
    at [email protected]/org.testng.TestClass.<init>(TestClass.java:42)
    at [email protected]/org.testng.TestRunner.initMethods(TestRunner.java:423)
    at [email protected]/org.testng.TestRunner.init(TestRunner.java:250)
    at [email protected]/org.testng.TestRunner.init(TestRunner.java:220)
    at [email protected]/org.testng.TestRunner.<init>(TestRunner.java:161)
    at [email protected]/org.testng.SuiteRunner$DefaultTestRunnerFactory.newTestRunner(SuiteRunner.java:578)
    at [email protected]/org.testng.SuiteRunner.init(SuiteRunner.java:185)
    at [email protected]/org.testng.SuiteRunner.<init>(SuiteRunner.java:131)
    at [email protected]/org.testng.TestNG.createSuiteRunner(TestNG.java:1383)
    at [email protected]/org.testng.TestNG.createSuiteRunners(TestNG.java:1363)
    at [email protected]/org.testng.TestNG.runSuitesLocally(TestNG.java:1217)
    at [email protected]/org.testng.TestNG.runSuites(TestNG.java:1144)
    at [email protected]/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 [email protected]/org.testng.internal.ObjectFactoryImpl.newInstance(ObjectFactoryImpl.java:22)
    ... 48 more

It seems a bit confusing to me, as io.github.sskorol.testcases is a part of src/test/java and there's no module-info for tests. So I can't export this package to TestNG. Have an assumption that the root cause in a TestNG reflection usage within ObjectFactoryImpl against test classes.

Does anyone have any idea how to workaround it?

Environment: JDK 9 (build 9+181), Gradle 4.1, TestNG 6.11

like image 961
Sergey Korol Avatar asked Aug 14 '17 11:08

Sergey Korol


2 Answers

Have an assumption that the root cause in a TestNG reflection usage within ObjectFactoryImpl against test classes.

It's one of two causes, yes. The other is that, apparently, Gradle runs your tests as a module. As you point out, there's no module descriptor for your tests. Gradle may use --patch-module to add the tests to the module containing the production code.

This question and answer provides a lot of background information and possible fixes. As a short term fix, I recommend to add opens io.github.sskorol.testcases to your production code's module descriptor. Judging by its name, I'd guess there is no such package yet, so you'd either have to rename or add a dummy class (I would prefer the former).

I would also take this issue to a Gradle mailing list or bug tracker. Unless we've overlooked something (entirely possible), Gradle's behavior is very unfortunate because it would require adapting the production code's module descriptor to the test code's needs.

like image 134
Nicolai Parlog Avatar answered Nov 12 '22 13:11

Nicolai Parlog


If the tests are in the same package as the module under test then they need to be compiled (with --patch-module) so that they are compiled "as if" they are part of the module. The references to TestNG types in the tests means that that --add-reads io.github.sskorol=testng will be needed too.

Running is similar. The tests need to be run "as if" they are in module io.github.sskorol. This means running with:

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

Additionally, you may need to export or open the packages with the tests to TestNG. This will come down to whether the test classes and methods are public in an exported package. To avoid scanning, the simplest is to open the all packages containing tests to TestNG, e.g.

--add-opens io.github.sskorol/io.github.sskorol.core=testng \
--add-opens io.github.sskorol/io.github.sskorol.core.internal=testng

(.internal is just a filler for a non-exported package in the module)

All this might look complicated but it's something that the Maven Surefire plugin and also Gradle should do.

like image 43
Alan Bateman Avatar answered Nov 12 '22 12:11

Alan Bateman