-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
@MethodSource
fails to load class with custom ClassLoader
arrangement
#3279
Comments
Could be a red herring but there's also a suppressed exception that could be related to #3131 or #3266:
|
There were definitely some changes to @chrisr3 Could you please raise an issue with the BND folks and cross-reference it here? |
Done. My first impression after examining the exception stack trace was that JUnit was (suddenly?) expecting the test class to be found in the Application classloader: Caused by: java.lang.ClassNotFoundException: org.testing.junit.TestJUnit
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
at org.junit.platform.commons.util.ReflectionUtils.lambda$tryToLoadClass$9(ReflectionUtils.java:831)
... I would consider this to be unlikely when executed within an OSGi framework. According to 07f2bf349e45f5a63228cb6002ae7bdbd3445ce1 is the first bad commit
commit 07f2bf349e45f5a63228cb6002ae7bdbd3445ce1
Author: Sam Brannen <sam@sambrannen.com>
Date: Sat Apr 22 12:20:31 2023 +0200
Treat overloaded local & external @MethodSource factory methods equally |
FWIW this small change fixes my test-case: diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java
index ddcb16b5c4..ba3d9d4874 100644
--- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java
+++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/provider/MethodArgumentsProvider.java
@@ -108,7 +108,7 @@ class MethodArgumentsProvider extends AnnotationBasedArgumentsProvider<MethodSou
String className = methodParts[0];
String methodName = methodParts[1];
String methodParameters = methodParts[2];
- Class<?> clazz = loadRequiredClass(className);
+ Class<?> clazz = testMethod.getDeclaringClass();
// Attempt to find an exact match first.
Method factoryMethod = ReflectionUtils.findMethod(clazz, methodName, methodParameters).orElse(null); The problem is that If Class<?> clazz = loadRequiredClass(className, testMethod.getDeclaringClass().getClassLoader()); where private static Class<?> loadRequiredClass(String className, ClassLoader classLoader) {
return ReflectionUtils.tryToLoadClass(className, classLoader).getOrThrow(
cause -> new JUnitException(format("Could not load class [%s]", className), cause));
} will work instead? |
With 5.9.2, putting the @chrisr3 Would you have time to submit a PR for that against |
Sorry, I am unable to build JUnit because I do not have "Java Flight Recorder", and have no idea where to get it from. I was only able to bisect by deleting the |
Curious, which distribution of JDK 17 do you use? |
$ java -version
openjdk version "17.0.6" 2023-01-17
OpenJDK Runtime Environment (Red_Hat-17.0.6.0.10-1.fc37) (build 17.0.6+10)
OpenJDK 64-Bit Server VM (Red_Hat-17.0.6.0.10-1.fc37) (build 17.0.6+10, mixed mode, sharing) |
For reference: #1987, #2104, #2106. Both of these issues were resolved without actually being completed, because they were going to be addressed in #806, but unfortunately #806 is still pending. My proposed solution in #2106 is essentially the same as those that @chrisr3 is suggesting here. In general, I think that using the test class' class loader would be the best solution for these types of problems. In the OSGi context, this puts the power back in the hands of the test developer because they can control which classes/packages are visible to the test class (and hence to the test class' classloader). In the case of (eg) a If I have a look at this in Bnd I might be able to find a workaround, as I have for the other issues (like #2104). But my workarounds essentially involve the Bnd tester doing more and more of the class loading on JUnit's behalf in order to overcome these issues, and I think I've probably picked all the low-hanging fruit - the issues are becoming increasingly difficult to work around as they are being found increasingly deeper inside JUnit. It would be nicer and cleaner if these issues were resolved in JUnit. |
Aha! I have found Java Flight Recorder in this package:
I had assumed that installing the package containing the pre-compiled shared objects was enough:
As indeed it appears to be, for everything except Java Flight Recorder. See #3280. |
I realise that I should have been a bit more specific. I think that this:
...is the best solution. There's no point loading the class when it's already been loaded. |
Heh, I did wonder about that 😉. But it also occurred to me that the method might exist on a parent class. |
That solution would break using a static method in another class, though. So I think using the test class' class loader to load the class would work in both cases and would be a no-op (since class loaders cache classes that have already been loaded) when |
I don't think that will matter. The parent class will be visible to the subclass.
Yes, you're right. So @chrisr3 's second implementation would work for both cases. The overhead in "reloading" the already-loaded test class for unqualified method names is small and is justified by the simplification it makes to the code. Perhaps then we might need a usage note for OSGi users somewhere, to make sure that if you're using a fully-qualified reference to a static method in another class, your test bundle must import the package that the class lives in. |
@MethodSource
fails to load class inside OSGi framework
@MethodSource
fails to load class inside OSGi framework@MethodSource
fails to load class with custom ClassLoader
arrangement
Prior to this commit, the extensions supporting @MethodSource, @EnabledIf, and @DisabledIf failed to load an external class if the external class was not visible to JUnit's default ClassLoader. This commit addresses those issues by loading external classes using the ClassLoader obtained from the test class or falling back to JUnit's default ClassLoader if the test's ClassLoader cannot be obtained. This allows JUnit to find the classes while running in a custom ClassLoader arrangement -- for example, inside an OSGi framework. Closes junit-team#3292 Closes junit-team#3279 Closes junit-team#3280
JUnit tests with
@MethodSource
fail inside an OSGi framework after upgrading from 5.9.2 to 5.9.3.Steps to reproduce
Run attached Gradle project:
Context
Jupiter 5.9.3
Platform 1.9.3
Gradle 8.1.1
Java 11
The text was updated successfully, but these errors were encountered: