Skip to content
Advertisement

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

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?

Advertisement

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.

Advertisement