How to validate that a method annotation is using an attribute with an specific value using archunit

Tags: , ,



I have an @Audit annotation, it has many optional attributes, I need to enforce the use of one boolean attribute useAccount = true for certain packages.

I am trying to use archunit to accomplish this validation, that way whenever a developer commits code that breaks the rule the CI will break and inform the team.

This would break the build:

@Audit
public myMethod(...) {
...
}

This is the right way:

@Audit(useAccount = true)
public myMethod(...) {
...
}

The problem is that Archunit doesn’t currently support asserting over methods. I was expecting to do something like:

methods().that().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)

Then my custom condition attributeCheckCondition would take care of looking into the attribute value.

Is there a way of retrieving methods as we retrieve classes? Without having to write a more complicated predicate and condition?

Answer

Update

Since ArchUnit 0.10.0 it is possible to create rules for members.

methods().that().areDeclaredInClassesThat().resideInAnyPackage("..controllers..", "..service..").and().areAnnotatedWith(Audit.class).should(attributeCheckCondition)

See also Composing Member Rules in the User Guide.

Original Answer

Since there are currently no basic rule definitions available for methods, an intermediate step is necessary. ArchUnit has a ClassesTransformer to transform JavaClasses into a collection of other types.

ClassesTransformer<JavaMethod> methods = new AbstractClassesTransformer<JavaMethod>("methods") {
    @Override
    public Iterable<JavaMethod> doTransform(JavaClasses javaClasses) {
        Set<JavaMethod> allMethods = new HashSet<>();
        for (JavaClass javaClass : javaClasses) {
            allMethods.addAll(javaClass.getMethods());
        }
        return allMethods;
    }
};

This ClassesTransformer can then be used as a base for custom rule definitions.

ArchRule rule = ArchRuleDefinition.all(methods).that(owner(resideInAnyPackage("..controllers..", "..service.."))).and(annotatedWith(Audit.class)).should(haveAttributeValue());
rule.check(javaClasses);

See also Rules with Custom Concepts in the User Guide and this issue.



Source: stackoverflow