junit-team / junit5

✅ The 5th major version of the programmer-friendly testing framework for Java and the JVM

Home Page:https://junit.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`@AutoClose` cannot invoke methods due to strict encapsulation with the module system

bjmi opened this issue · comments

Running following test

class AutoCloseTest {
    @AutoClose
    InputStream inputStream = InputStream.nullInputStream();

    @AutoClose("shutdown")
    ExecutorService service = Executors.newSingleThreadExecutor();

    @Test
    void test() { }
}

results in:

java.lang.reflect.InaccessibleObjectException: Unable to make public void java.io.InputStream$1.close() throws java.io.IOException accessible: module java.base does not "opens java.io" to unnamed module @cc43f62
java.lang.reflect.InaccessibleObjectException: Unable to make public void java.util.concurrent.Executors$AutoShutdownDelegatedExecutorService.shutdown() accessible: module java.base does not "opens java.util.concurrent" to unnamed module @cc43f62

In a nutshell

InputStream.class.getMethod("close").invoke(inputStream);      // works
inputStream.getClass().getMethod("close").invoke(inputStream); // fails java.lang.IllegalAccessException: class AutoCloseTest cannot access a member of class java.io.InputStream$1 (in module java.base) with modifiers "public"

Some research revealed:

I have to walk the interfaces + subclasses then to find a method that works.

OpenJDK 21.0.2 for Windows
JUnit 5.11.0-SNAPSHOT

Thanks for bringing this to our attention, @bjmi! 👍

I'm very familiar with Spring's ClassUtils.getInterfaceMethodIfPossible() approach. We introduced that to be compatible with the Java Module System.

So, it looks like we probably need that in JUnit as well.

I'll look into it!

Please note that I have removed the milestone assignment since @AutoClose has not yet been released.

Just one consideration: is it sufficient to only search for a corresponding interface method?

Abstract base classes e.g. java.io.InputStream (< Java 1.5), java.nio.Buffer, or java.util.ResourceBundle function in a similar way to interfaces.

Shouldn't they also be supported?

Hi @bjmi,

That's a valid question, so thanks for bringing it up.

I suppose that one might ideally want to find the first matching method which is declared in a public type (for example, a class, abstract class, or interface).

However, my primary goal was to support the most common use cases for @AutoClose methods which are likely one of the following.

  • AutoCloseable#close
  • a method defined in a public interface which may potentially reside in a different module
  • a method defined in the same module as the test class (or in a module that has opened itself up for reflection to the test's module) which may be invoked via reflection

In that regard, I think we have the major use cases covered currently.

However, if people start complaining about JUnit not being able to invoke @AutoClose methods on private types in different modules where the method is actually publicly defined in a public superclass, we may consider enhancing the method search/invocation algorithm.

Cheers,

Sam