Certain frameworks require the empty constructor of a class to be present, e.g. a JakartaEE stateless service, but they do not have the appropriate dependencies injected. I would like to annotate these constructors with something like a DoNotUse
annotation (with a reason), and then use ArchUnit to check if no one is actually using it.
Something like this:
ArchRuleDefinition.noConstructors()
.that()
.areAnnotatedWith(DoNotUse.class)
.should()
.beCalled();
But beCalled
or beUsed
AFAIK does not exist. I solved it now by adding a never matching condition:
ArchRuleDefinition.constructors()
.that()
.areAnnotatedWith(DoNotUse.class)
.should()
.onlyBeCalled()
.byClassesThat()
.haveSimpleName("ThisOnPurposeDoesNotMatchAnything");
How do I write a beCalled
or the inverse neverBeCalled
?
You can use a custom condition using convenient factory methods for ArchCondition
s and DescribedPredicate
s:
import static com.tngtech.archunit.base.DescribedPredicate.describe;
import static com.tngtech.archunit.lang.conditions.ArchConditions.be;
// ...
ArchRule rule = ArchRuleDefinition.noConstructors()
.that().areAnnotatedWith(DoNotUse.class)
.should(be(describe("called", ctor -> !ctor.getCallsOfSelf().isEmpty())));
If you want more descriptive error messages indicating where the unwanted call happens (cf. comment), you can use a custom ArchCondition
like this:
import static com.tngtech.archunit.lang.SimpleConditionEvent.satisfied;
// ...
ArchRule rule = ArchRuleDefinition.noConstructors()
.that().areAnnotatedWith(DoNotUse.class)
.should(new ArchCondition<JavaConstructor>("be called") {
@Override
public void check(JavaConstructor constructor, ConditionEvents events) {
constructor.getCallsOfSelf().forEach(call ->
events.add(satisfied(call, call.getDescription()))
);
}
});