ArchUnit
ArchUnit copied to clipboard
Naming convention test of a class containing lambda with switch and enum
In a Spring Boot 2.7 project I implemented the following code:
package com.app.archunittest.constant;
public enum DummyEnum {
FIRST,
SECOND,
THIRD
}
package com.app.archunittest.service;
import com.app.archunittest.constant.DummyEnum;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class DummyService {
private DummyEnum dummyEnum = DummyEnum.FIRST;
public void dummyMethod() {
Optional.ofNullable(dummyEnum)
.ifPresent(de -> {
switch(de) {
case FIRST:
System.out.println("First item selected.");
break;
case SECOND:
System.out.println("Second item selected.");
break;
case THIRD:
System.out.println("Third item selected.");
break;
}
});
}
}
Then, with ArchUnit 1.0.1 I created a test for it:
import com.tngtech.archunit.junit.AnalyzeClasses;
import com.tngtech.archunit.junit.ArchTest;
import com.tngtech.archunit.lang.ArchRule;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
@AnalyzeClasses(packages = "com.app.archunittest")
public class NamingConventionTest {
@ArchTest
static ArchRule serviceShouldBeSuffixed = classes()
.that()
.resideInAPackage("..service..")
.should()
.haveSimpleNameEndingWith("Service");
}
The test fails with an error:
java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - 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)
Then I created a custom annotation @IgnoreArchUnitTest:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface IgnoreArchUnitTest {
}
and applied it to my service class. Modifiled the test:
@ArchTest
static ArchRule serviceShouldBeSuffixed = classes()
.that()
.resideInAPackage("..service..")
.and()
.areNotAnnotatedWith(IgnoreArchUnitTest.class)
.should()
.haveSimpleNameEndingWith("Service");
Service class should now be ignored by the test, but the error message I got was almost the same:
java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'classes that reside in a package '..service..' and are not annotated with @IgnoreArchUnitTest 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)
Ok, the error message rightly says that the Class <com.app.archunittest.service.DummyService$1> does not have simple name ending with 'Service'. Thus, I removed the @IgnoreArchUnitTest annotation both from the service class and from the test. Added a new condition to the test for checking the "Service$1" suffix:
@ArchTest
static ArchRule serviceShouldBeSuffixed = classes()
.that()
.resideInAPackage("..service..")
.should()
.haveSimpleNameEndingWith("Service")
.orShould()
.haveSimpleNameEndingWith("Service$1");
Now, the error says:
java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'classes that reside in a package '..service..' should have simple name ending with 'Service' or should have simple name ending with 'Service$1'' was violated (1 times): Class <com.app.archunittest.service.DummyService$1> does not have simple name ending with 'Service$1' in (DummyService.java:0) and Class <com.app.archunittest.service.DummyService$1> does not have simple name ending with 'Service' in (DummyService.java:0)
"Class <com.app.archunittest.service.DummyService$1> does not have simple name ending with 'Service$1'" That's interesting...
However, when I modified my service method as below:
public void dummyMethod() {
Optional.ofNullable(dummyEnum)
.ifPresent(de -> {
switch(de.name()) {
case "FIRST":
System.out.println("First item selected.");
break;
case "SECOND":
System.out.println("Second item selected.");
break;
case "THIRD":
System.out.println("Third item selected.");
break;
}
});
}
by replacing enum values with corresponding strings within the switch statement, the test passed successfully.
Is that a correct behavior of that test or maybe I did something wrong?
The synthetic class com.app.archunittest.service.DummyService$1
doesn't have a simple name (but ""
).
Technically, you could use orShould().haveNameMatching(".*\\.Service\\$1")
, but you probably want to use the solution classes().that().doNotHaveModifier(JavaModifier.SYNTHETIC)
from https://github.com/TNG/ArchUnit/issues/1011#issuecomment-1336332226.
SYNTHETIC doesn't work for me, but i've noticed that these generated classes are recognised as anonymous. As a workaround i use .areNotAnonymousClasses() to exclude them from the evaluation