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
?
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
?
- why create a method/constructor that should never be called? – Stultuske Commented Mar 13 at 12:29
- you can usually declare those empty constructors to be private, the frameworks will stick pick them up – Rick Commented Mar 13 at 12:40
- Some frameworks do require public default constructors. Even if it could be avoided in this case, it's a valid question. – kapex Commented Mar 13 at 12:42
- 1 I already explained that: frameworks like JakartaEE require those constructors. When using WildFly you can suffice with a protected constructor (because the generated proxies will extend the class), not private. But then IntelliJ will place an error marker on those classes and your code base is all red. – tbeernot Commented Mar 13 at 12:46
- I don't have an answer but sometimes it helps to reverse the order, even though it look less intuitive. Instead "this should not be called" reverse it to "nothing should call this" e.g. create a rule: no classes should call constructors annotated with DoNotUse. – kapex Commented Mar 13 at 13:02
1 Answer
Reset to default 0You 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()))
);
}
});