How can I enforce using Archunit that every class has a correspondent test class?
I'm able to get all the class that I want to check using this Rule:
classes().that()
.areNotInterfaces()
.and()
.areNotAnonymousClasses()
.and()
.areNotRecords()
.and()
.areNotEnums()
.should()
But not sure where to go from here.
Thanks
You can implement a "non-local" ArchCondition
(i.e. one that relies on all objects to test) by overwriting its init(Collection<T> allObjectsToTest)
method.
ArchUnit's library of GeneralCodingRules
has a testClassesShouldResideInTheSamePackageAsImplementation()
, whose implementation is a good starting point to come up with something like this:
@ArchTest
static final ArchRule relevant_classes_should_have_tests =
classes()
.that()
.areTopLevelClasses()
.and().areNotInterfaces()
.and().areNotRecords()
.and().areNotEnums()
.should(haveACorrespondingClassEndingWith("Test"));
private static ArchCondition<JavaClass> haveACorrespondingClassEndingWith(String testClassSuffix) {
return new ArchCondition<JavaClass>("have a corresponding class with suffix " + testClassSuffix) {
Set<String> testedClasseNames = emptySet();
@Override
public void init(Collection<JavaClass> allClasses) {
testedClasseNames = allClasses.stream()
.map(JavaClass::getName)
.filter(className -> className.endsWith(testClassSuffix))
.map(className -> className.substring(0, className.length() - testClassSuffix.length()))
.collect(toSet());
}
@Override
public void check(JavaClass clazz, ConditionEvents events) {
if (!clazz.getName().endsWith(testClassSuffix)) {
boolean satisfied = testedClasseNames.contains(clazz.getName());
String message = createMessage(clazz, "has " + (satisfied ? "a" : "no") + " corresponding test class");
events.add(new SimpleConditionEvent(clazz, satisfied, message));
}
}
};
}