Search code examples
kotlintestingarchunit

ArchUnit: Test a class if it contains an annotated method


I want to write an archunit rule in my J2EE project to test when a class is annotated with @Startup, then it must contain a method that is annotated with @PostConstruct.

I tried this

  @ArchTest
  private val startUpBeansShouldHavePostConstruct: ArchRule = classes().that()
    .areAnnotatedWith(Startup::class.java)
    .shouldHave(methods().that().areAnnotatedWith(PostConstruct::class.java))
    .because("classes with @Startup should contain a @PostConstruct annotated method")

but that doesn't compile in the shouldHave()-line. I am a bit stuck how to define such a rule. My actual goal is to have a rule that says there should be exactly one annotated method.


Solution

  • You can conveniently use ArchConditions.have and DescribedPredicate.describe to define custom conditions and predicates:

    With

    import com.tngtech.archunit.base.DescribedPredicate.describe
    import com.tngtech.archunit.core.domain.JavaClass
    import com.tngtech.archunit.junit.ArchTest
    import com.tngtech.archunit.lang.conditions.ArchConditions.have
    import com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes
    

    you can use

    @ArchTest
    private val startUpBeansShouldHavePostConstruct =
        classes()
            .that().areAnnotatedWith(Startup::class.java)
            .should(have(oneMethodAnnotatedWith(PostConstruct::class.java)))
    
    private fun oneMethodAnnotatedWith(annotationClass: Class<out Annotation>) =
        describe("one method annotated @${annotationClass.simpleName}") {
            javaClass: JavaClass ->
                javaClass.methods.count { it.isAnnotatedWith(annotationClass) } == 1
        }