Search code examples
javatestingarchunit

ArchRule fails on proper test


I have test

@ArchTest
  public static final ArchRule daoShouldBeUsedOnlyByHelper = theClass(SegmentDAO.class).should()
      .onlyBeAccessed()
      .byClassesThat(simpleName(SegmentHelper.class.getSimpleName())).orShould()
      .onlyBeAccessed().byClassesThat(simpleName(SegmentHelperFake.class.getSimpleName()));

and the test fails like:

java.lang.AssertionError: Architecture Violation [Priority: MEDIUM] - Rule 'classes that have fully qualified name 'me.test.common.db.SegmentDAO' should only be accessed by classes that have fully qualified name 'me.test.common.helpers.db.SegmentHelper' or should only be accessed by classes that have fully qualified name 'me.test.common.test.SegmentHelperFake'' was violated (1 times):
Method <me.test.common.helpers.db.SegmentHelper.delete(int, int)> calls method <me.test.common.db.SegmentDAO.delete(int, int)> in (SegmentHelper.java:45) and Method <me.test.common.helpers.db.SegmentHelper.duplicate(int, int)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelper.java:51) and Method <me.test.common.helpers.db.SegmentHelper.get(int)> calls method <me.test.common.db.SegmentDAO.get(int)> in (SegmentHelper.java:29) and Method <me.test.common.helpers.db.SegmentHelper.get(int, int)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelper.java:38) and Method <me.test.common.helpers.db.SegmentHelper.insert(int, java.lang.String, java.sql.Timestamp, java.sql.Timestamp, int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.insert(int, java.lang.String, java.sql.Timestamp, java.sql.Timestamp, int, int, java.lang.String)> in (SegmentHelper.java:23) and Method <me.test.common.helpers.db.SegmentHelper.removePartByTrackerId(int, int)> calls method <me.test.common.db.SegmentDAO.removePartByTracker(int, int)> in (SegmentHelper.java:72) and Method <me.test.common.helpers.db.SegmentHelper.updateFilters(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelper.java:78) and Method <me.test.common.helpers.db.SegmentHelper.updateFilters(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.updateFilters(int, int, java.lang.String)> in (SegmentHelper.java:84) and Method <me.test.common.helpers.db.SegmentHelper.updateName(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelper.java:61) and Method <me.test.common.helpers.db.SegmentHelper.updateName(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.updateName(int, int, java.lang.String)> in (SegmentHelper.java:67) and Method <me.test.common.test.SegmentHelperFake.delete(int, int)> calls method <me.test.common.db.SegmentDAO.delete(int, int)> in (SegmentHelperFake.java:49) and Method <me.test.common.test.SegmentHelperFake.duplicate(int, int)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelperFake.java:56) and Method <me.test.common.test.SegmentHelperFake.get(int)> calls method <me.test.common.db.SegmentDAO.get(int)> in (SegmentHelperFake.java:31) and Method <me.test.common.test.SegmentHelperFake.get(int, int)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelperFake.java:41) and Method <me.test.common.test.SegmentHelperFake.insert(int, java.lang.String, java.sql.Timestamp, java.sql.Timestamp, int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.insert(int, java.lang.String, java.sql.Timestamp, java.sql.Timestamp, int, int, java.lang.String)> in (SegmentHelperFake.java:24) and Method <me.test.common.test.SegmentHelperFake.updateFilters(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelperFake.java:79) and Method <me.test.common.test.SegmentHelperFake.updateFilters(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.updateFilters(int, int, java.lang.String)> in (SegmentHelperFake.java:85) and Method <me.test.common.test.SegmentHelperFake.updateName(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.get(int, int)> in (SegmentHelperFake.java:67) and Method <me.test.common.test.SegmentHelperFake.updateName(int, int, java.lang.String)> calls method <me.test.common.db.SegmentDAO.updateName(int, int, java.lang.String)> in (SegmentHelperFake.java:73)

But in mentions method calls that should be able to call it. Not sure what is w rong


Solution

  • A rule (in pseudo code)

    // ...
        .should().a()
        .orShould().b();
    

    will only pass when a or b applies.

    In your case, neither condition a ("only be accessed by SegmentHelper") nor b ("only be accessed by SegmentHelperFake") applies on their own (as you seem to have accesses by both SegmentHelper and SegmentHelperFake).

    You probably want to define another condition that already combines the options:

    @ArchTest
    ArchRule daoShouldBeUsedOnlyByHelper = theClass(SegmentDAO.class)
        .should().onlyBeAccessed().byClassesThat(are(
            equivalentTo(SegmentHelper.class)
                .or(equivalentTo(SegmentHelperFake.class))
        ));
    

    using

    import static com.tngtech.archunit.core.domain.JavaClass.Predicates.equivalentTo;
    import static com.tngtech.archunit.lang.conditions.ArchPredicates.are;