How to get list of inner classes from JavaClass
I'm trying to write an ArchCondition that checks if all inner classes of a class have the same annotation, but with distinct values. However, I fail to see how I can access a list of inner classes of a JavaClass.
I've tried JavaClass.members and JavaClass.allMembers without result. I've scrolled through the list of fields and methods on JavaClass, but I can't seem to find the right one if it exists.
Could someone point me in the right direction?
Thanks in advance!
JavaClass.getAllSubclasses() might solve your issue, I am using 0.23.1.
I'm not aware that ArchUnit provides a direct API to obtain all inner / nested classes (note that there's a subtle difference) of a given JavaClass, but you can easily collect them from all imported JavaClasses:
@AnalyzeClasses(packagesOf = {Issue974.class})
class Issue974 {
@ArchTest
void nestedClassesShouldHaveDistinctAnnotations(JavaClasses classes) {
Class<Issue974> outerClass = Issue974.class;
Set<JavaClass> nestedClasses = classes.stream()
.filter(javaClass -> javaClass.getEnclosingClass()
.map(enclosingClass -> enclosingClass.isEquivalentTo(outerClass))
.orElse(false)
)
.collect(toSet());
Set<String> annotationValues = nestedClasses.stream()
.flatMap(javaClass -> javaClass.tryGetAnnotationOfType(Annotation.class)
.map(Annotation::value)
.asSet()
.stream()
)
.collect(toSet());
assertThat(nestedClasses)
.as("nested classes of " + outerClass)
.hasSameSizeAs(annotationValues);
}
@Annotation("I1")
class I1 {
}
@Annotation("I2")
class I2 {
}
}
@interface Annotation {
String value();
}
(I'm using AssertJ's .hasSameSizeAs.)
I'm afraid this is not that convenient at the moment :thinking: Because as mentioned the link JavaClass -> inner class is missing (I actually planned on adding it, but haven't found the time yet).
If you want to solve this, then as @hankem pointed out your best bet likely is to use the link JavaClass -> enclosing class, which is currently present.
If you want to write this as a standard ArchCondition it could for example look something like this:
new ArchCondition<JavaClass>("have inner classes that ...") {
private final Map<JavaClass, Set<JavaClass>> outerClassesToInnerClasses = new HashMap<>();
@Override
public void init(Collection<JavaClass> allClasses) {
allClasses.forEach(possibleInnerClass ->
possibleInnerClass.getEnclosingClass()
.ifPresent(outerClass -> outerClassesToInnerClasses
.computeIfAbsent(outerClass, __ -> new HashSet<>())
.add(possibleInnerClass)));
}
@Override
public void check(JavaClass javaClass, ConditionEvents events) {
outerClassesToInnerClasses.getOrDefault(javaClass, Collections.emptySet())
.forEach(/* check inner classes of javaClass */);
}
};
Hey. I am trying to do a similar thing but with kotlin. I want to check that there is no class that inherits from specific class, that can have certain annotation. But I want to apply this logic also on all inner classes -> Consider nested test classes from Junit5
Consider MyClass which has additional 3 nested inner classes:
Code:
ArchRuleDefinition
.noClasses().that().areAssignableTo(MyClass::class.java)
.and(
describe("whatever") {
it.allSubclasses // returns empty via debugger but should not
}
)
.should().dependOnClassesThat().belongToAnyOf(WithMockUser::class.java)
.check(ArchUnit.testClasses)
@thugec, can you provide a minimal example of MyClass and its subclasses, and which violations you want to catch?
I'm sorry, but I didn't properly understand what you want to achieve. (Do you only want to test subclasses that are also inner classes, or both inner classes and sub classes? How does this relate to JUnit5's @Nested classes?)
@thugec, can you provide a minimal example of
MyClassand its subclasses, and which violations you want to catch? I'm sorry, but I didn't properly understand what you want to achieve. (Do you only want to test subclasses that are also inner classes, or both inner classes and sub classes? How does this relate to JUnit5's@Nestedclasses?)
I wanted to achieve that "forbidden" annotation cannot be also present on the declaration of the inner classes, not only on the top class. JavaClass returns also provide "members", but it only returns function inside a Test class, never the test functions for example. I cannot share code snippet due to company policy. Basically I am trying to find a way, how to get also list of inner classes in the kotlin for a particular top level class. I expected that:
public Set<JavaClass> getSubclasses() {
return subclasses;
}
does the trick, but obviously it does not.
Here is a very simple example:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MyClass {
@Nested
inner class TestA {}
@Nested
inner class TestB {}
}
I want to check if TestA and TestB does not contain certain annotations (or whatever else)