Lets say i have a method in some class in my application's package NetBeans project:
package wuzzle.woozle;
import org.contoso.frobber.grob.Whiztactular;
@Whiztactular
public void testFizBuzz() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package frob;
import org.contoso.frobber.grob.Whiztactular;
@Whiztactular
public void testfrobFizBuzz() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package grob;
import org.contoso.frobber.grob.Whiztactular;
@Whiztactular
public void testGrobZoom() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package contoso.gurundy;
import org.contoso.frobber.grob.Whiztactular;
@Whiztactular
public void testDingbatWoozle() {
if (1 != 0)
throw new Exception("Whiztactular failed");
throw new Exception("Whiztactular failed");
}
I want to:
- enumerate all classes/methods
- find methods tagged with a specified
@Annotation - construct the class
- call the (parameterless) method
How can i do this in Java?
In .NET it's easy
Here's how you do it in .NET (in pseudo-Java):
//Find all methods in all classes tagged with @Test annotation,
//and add them to a list.
List<MethodInfo> whiztactularMethods = new ArrayList<>();
//Enumerate all assemblies in the current application domain
for (Assembly a : AppDomain.currentDomain.getAssemblies()) {
//Look at each type (i.e. class) in the assembly
for (Type t : a.getTypes()) {
//Look at all methods in the class.
for (MethodInfo m : t.getMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
//If the method has our @Whiztactular annotation defined: add it
if (m.IsDefined(typeof(org.contoso.frobber.grob.Whiztactular), true))
whiztactularMethods .add(m);
}
}
}
And now that we have a List of all methods with the @Whiztactular annotation, it's just a matter of calling them:
//Call every test method found above
for (MethodInfo m : whiztactularMethods) {
Object o = Activator.CreateInstance(m.DeclaringType); //Construct the test object
m.Invoke(o, null); //call the parameterless Whiztactular method
}
What is the JRE equivalent of the above?
In Delphi it's easy to
When a Delphi application starts, the initializer of each unit is called:
initialization
WhiztactularRunner.registerWhiztactularClass(TWuzzleWoozle);
So then i can have all my test code register itself.
But Java doesn't have .java file initialization; nor does it have static constructors.
The Journey
I want JUnit to run tests
↓
JUnit requires tests to be in a special separate project
↓
Use reflection to find the test methods
↓
Reflection requires you to know the name of the packages that all developers have put their tests in
↓
Use Reflections library
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own Test Annotation, and use reflections to find all methods that are tagged with it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase annotation, and use reflections to find all classes that are tagged with it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase interface, and use reflections to find all classes that implement it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase class, and use reflections to find all classes that extend it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create a static list, and use a static class constructor to register the class with the my TestRunner
↓
Java doesn't have static class constructors
↓
Create a static list, and use the package initializer to register the class with the my TestRunner
↓
Java doesn't have package initializers
↓
Create a static list, and use the events to listen for when a package is loaded, and then register the package with my static list
↓
Java doesn't have package load events
↓
Enumerate all packages
↓
Reflection has no way to enumerate all packages
↓
Ask the class loader that loaded my current class for any other classes it has loaded
↓
Class loader won't know about classes until someone has actually needed them, and you might not even be using the same class loader instance
↓
Enumerate all packages in the current class path ⇐ in progress
↓
Enumerate all jar files on the local PC, use a custom class loader to load each one, then get a list of all packages in each one ⇐ in progress
↓
Spent 4 days so far trying to solve this problem that was solvable in .NET with 5 lines of code, and in Delphi with 3 lines of code
↓
Investigate converting 409 jsp, and 498 java code files to ASP.net and C# ⇐ in progress
↓
Give up on having automated unit, functional, and integration tests ⇐ in progress
Research Effort
Get all methods with a particular annotation in a package (Question isn't about the current package. Accepted answer uses 3rd party library.)
Java seek a method with specific annotation and its annotation element (Question is about a specific class, rather than finding the classes)
Get all methods with a particular annotation in a package (explains what a package is)
How to find annotated methods in a given package? (explains what a package is) Additional research effort
java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory even though I have the right dependencies
how to register a java class if the static initializer isn't called till the class is referenced
- Dynamic object registration in Java
- getConstructor with no parameters
- Load Jar dynamically and register class(es) in applicationContext at runtime
- Is it possible to determine descendants solely through Java reflection API?
- Call Methods at Runtime Using Java Reflection
- JavaDocs - Invoking Methods
- At runtime, find all classes in a Java application that extend a base class
- Default access modifier for a Java constructor
- Find Java classes implementing an interface
- Finding all classes implementing a specific interface
- How does JUnit find tests?
- Book: Unit Testing in Java
- 2/28/1998: JUnit 1.0
- JUnit Cookbook
- How can I get a list of all the implementations of an interface programmatically in Java?
- How can I get all Class files in a specific package in Java?
- Class Loaders in Java
- How can I enumerate all classes in a package and add them to a List?
- Java Reflection - Get List of Packages
- Getting the list of packages in a java project
- Tool to convert java to c# code
- Package Initialization in Java
- How to write a package-level static initializer in Kotlin?
- https://stackoverflow.com/questions/72795950/java-initialize-all-classes-in-package-without-knowing-names
- https://github.com/classgraph/classgraph
- What is an initialization block?
- Package Initialization in Java
Launcherto execute the tests. But if you really want to go low level, it’sReflectionSupportmight have the right methods for you.ServiceLoaderis a nice thing when you use modules, as then, you don’t use configuration files anymore; it’s part of the language. E.g. you declareprovides service.Type with implementation.Type;in the module-info and the compiler will verify that all constraints are met. But it wouldn’t help you with your specific case, as there is no service type (interface or abstract base class) extended by your test classes.ReflectionSupportI have linked would require you to know the package, e.g. this method only requires you to know the class path entry.@Test:for(String cpEntry: System.getProperty("java.class.path").split(File.pathSeparator)) { for(var cl: ReflectionSupport.findAllClassesInClasspathRoot(Paths.get(cpEntry) .toUri(), cl -> Arrays.stream(cl.getMethods()).anyMatch(m -> m.isAnnotationPresent(Test.class)), str -> true)) { System.out.println(cl); } }