-
Notifications
You must be signed in to change notification settings - Fork 325
Description
New users of ArchUnit, including myself, are often confused when a seemingly correct rule fails on a class they didn't write, typically one ending with $1, $2, etc. (e.g., MyService$1).
As detailed in issue #1019, this problem occurs because the Java compiler generates synthetic or anonymous classes for certain language features like lambdas, switch expressions on enums, or try-with-resources statements.
The current error message is technically correct but unhelpful, as it points to a compiler artifact without any context:
java.lang.AssertionError: Architecture Violation [...]
Rule 'classes that reside in a package '..service..' should have simple name ending with 'Service'' was violated (1 times):
Class <com.app.archunittest.service.DummyService$1> does not have simple name ending with 'Service' in (DummyService.java:0)
This leads new users to spend significant time debugging an issue that isn't an actual architectural violation in their source code. The solution—adding .and().doNotHaveModifier(JavaModifier.SYNTHETIC) or .and().areNotAnonymousClasses()—is not immediately obvious.
Describe the solution you'd like
I propose enhancing the error message to detect when a failing class is synthetic or anonymous and provide a helpful hint to the user. This would guide them directly to the correct solution and dramatically improve the new user experience.
Here is an example of the proposed error message:
java.lang.AssertionError: Architecture Violation [...]
Rule 'classes that reside in a package '..service..' should have simple name ending with 'Service'' was violated (1 times):
Class <com.app.archunittest.service.DummyService$1> does not have simple name ending with 'Service' in (DummyService.java:0)
Hint: The failing class 'DummyService$1' appears to be a synthetic class generated by the compiler from a lambda or other language feature. To test only developer-written code, consider adding `.and().doNotHaveModifier(JavaModifier.SYNTHETIC)` or `.and().areNotAnonymousClasses()` to your rule.
Describe alternatives you've considered
The current alternative is for users to discover the solution through trial and error, or by finding existing GitHub issues like #1019. This is not an ideal user experience. Adding a proactive hint in the error message is a far more effective approach.
Here is a minimal reproducible example from issue #1019 that triggers the confusing error.
Code that causes the issue:
@Service
public class DummyService {
private DummyEnum dummyEnum = DummyEnum.FIRST;
public void dummyMethod() {
Optional.ofNullable(dummyEnum)
.ifPresent(de -> {
// This switch on an enum inside a lambda generates a synthetic class
switch(de) {
case FIRST: System.out.println("First"); break;
case SECOND: System.out.println("Second"); break;
case THIRD: System.out.println("Third"); break;
}
});
}
}ArchUnit Test:
@AnalyzeClasses(packages = "com.app.archunittest")
public class NamingConventionTest {
@ArchTest
static ArchRule serviceShouldBeSuffixed = classes()
.that()
.resideInAPackage("..service..")
.should()
.haveSimpleNameEndingWith("Service");
}Implementing this small improvement would make ArchUnit more intuitive and save developers time, reinforcing its role as a helpful tool rather than a source of confusion.