Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LauncherInterceptor does not replace the ClassLoader for all tests #3392

Closed
holly-cummins opened this issue Jul 11, 2023 · 3 comments
Closed

Comments

@holly-cummins
Copy link

holly-cummins commented Jul 11, 2023

I'm trying out JUnit 5.10-RC1. The description of the new LauncherInterceptor API on #201 is

... the upcoming 5.10 release will introduce LauncherInterceptor (see #3091 for details) and there's an example for replacing the class loader for all tests in the User Guide.

I'm finding that the interceptor gets called at an early phase in the lifecycle, but setting the TCCL does not seem to affect what classloader is used for test class loading. Is there an extra step I'm missing?

Steps to reproduce

I'm attaching a simple reproducer. It's very similar to the documented example. I make a launcher interceptor which sets a new classloader on the thread context classloader. I can see, from printlns, that the interceptor is being called.

 public CustomLauncherInterceptor() throws Exception {

        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        customClassLoader = new ClassLoader("SuperCustom", parent) {};

    }

    @Override
    public <T> T intercept(Invocation<T> invocation) {

        Thread currentThread = Thread.currentThread();
        ClassLoader originalClassLoader = currentThread.getContextClassLoader();
        currentThread.setContextClassLoader(customClassLoader);
        try {
            return invocation.proceed();
        }
        finally {
            currentThread.setContextClassLoader(originalClassLoader);
        }
    }

In my test, I check what classloader the test class was loaded with:

 @Test
    public void testClassLoader() throws URISyntaxException, IOException, InterruptedException {
        String name = this.getClass().getClassLoader().getName();
        assertEquals("SuperCustom", name);
    }

The test fails:

[ERROR] Failures: 
[ERROR]   SimpleTest.testClassLoader:13 expected: <SuperCustom> but was: <app>

Resources

Context

  • Used versions (Jupiter/Vintage/Platform): Jupiter 5.10.0-RC1
  • Build Tool/IDE: Maven/IDEA
@marcphilipp marcphilipp added this to the 5.10 GA milestone Jul 11, 2023
@sbrannen sbrannen changed the title LauncherInterceptor does not replace the class loader for all tests LauncherInterceptor does not replace the ClassLoader for all tests Jul 13, 2023
@holly-cummins
Copy link
Author

holly-cummins commented Jul 14, 2023

I think we might be able to close this. @stuartwdouglas has pointed out that my test could never pass, because the child classloader has no resources attached to it and has no custom behaviour, so it would always delegate to the parent. That means the reported classloader would always be the parent classloader.

With this revised interceptor constructor, the classloader gets to do more, and my test does pass:

public CustomLauncherInterceptor() throws Exception {
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        customClassLoader = new ClassLoader("SuperCustom", parent) {

            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException {
                return loadClass(name, true);
            }

            @Override
            protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
                var ex = findLoadedClass(name);
                if (ex != null) {
                    return ex;
                }
                var res = getResource(name.replace(".", "/") + ".class");
                if (res != null && res.getProtocol().equals("file")) {
                    try (var in = res.openStream()) {
                        var data = in.readAllBytes();
                        return defineClass(name, data, 0, data.length);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }

                return super.loadClass(name, resolve);
            }
        };
    }

Apologies for the noise!

@sbrannen
Copy link
Member

Hi Holly,

Thanks for the feedback, and we're glad to know you sorted it out!

Cheers,

Sam

@sbrannen sbrannen closed this as not planned Won't fix, can't repro, duplicate, stale Jul 15, 2023
@sbrannen sbrannen removed this from the 5.10 GA milestone Jul 15, 2023
@JohnZ1385
Copy link

just out of curiosity has anyone managed to get this working for tests within a WAR file? See my stackoverflow post
https://stackoverflow.com/questions/77084650/invoking-junit-within-a-webapp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants