TNG / ArchUnit

A Java architecture test library, to specify and assert architecture rules in plain Java
http://archunit.org
Apache License 2.0
3.19k stars 289 forks source link

ArchUnit do not correctly check Annotation on enum Constants #1312

Closed danibs closed 4 months ago

danibs commented 4 months ago

Hi! I have an Enum like:

public enum MyModeStatus {
   @ErrorDescription( "Undefined Error" )
   UNDEFINED,

   ANOTHER_VALUE
}

I want to find that ANOTHER_VALUE doesn't have @ErrorDescription. I'm trying with:

@ArchTest
public static final ArchRule statusFieldsAnnotation = ArchRuleDefinition.fields().that()
   .areDeclaredInClassesThat().areEnums()
   .and().areDeclaredInClassesThat().haveSimpleNameEndingWith( "ModeStatus" )
   .should().beAnnotatedWith( ErrorDescription.class );

but ArchUnit said:

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'fields that are declared in classes that are enums and are declared in classes that have simple name ending with 'ModeStatus' should be annotated with @ErrorDescription' was violated (1 times):
Field <xx.yy.MyModeStatus.ENUM$VALUES> is not annotated with @ErrorDescription in (MyModeStatus.java:0)

I tryied with version 1.2.1 and the last one 1.3.0.

Any suggestion? Thanks!

hankem commented 4 months ago
Looking at the bytecode – according to `javap -c -p -v MyModeStatus.class`: ``` [...] public static final MyModeStatus UNDEFINED; descriptor: LMyModeStatus; flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM RuntimeInvisibleAnnotations: 0: #16(#17=s#18) ErrorDescription(value="Undefined Error") public static final MyModeStatus ANOTHER_VALUE; descriptor: LMyModeStatus; flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM private static final MyModeStatus[] $VALUES; descriptor: [LMyModeStatus; flags: (0x101a) ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC [...] ```

it seems that the enum fields have a special flag, whereas the compiler-generated field $VALUES is marked as synthetic.

To only select the actual enum fields, you can therefore extend your that() clause with

    .and().haveModifier(JavaModifier.ENUM)

or

    .and().doNotHaveModifier(JavaModifier.SYNTHETIC)
danibs commented 4 months ago

Perfect @hankem , you got the problem and your suggestion works!

Thanks so much!