ArchRule规则校验异常:合法调用类触发违规提示
问题:ArchTest规则误报架构违规,允许的调用被标记为违规
我编写了如下ArchTest规则,要求SegmentDAO类仅允许被SegmentHelper和SegmentHelperFake类访问:
@ArchTest public static final ArchRule daoShouldBeUsedOnlyByHelper = theClass(SegmentDAO.class).should() .onlyBeAccessed() .byClassesThat(simpleName(SegmentHelper.class.getSimpleName())).orShould() .onlyBeAccessed().byClassesThat(simpleName(SegmentHelperFake.class.getSimpleName()));
但执行测试时出现架构违规报错,报错信息中列出的调用均为规则允许的类发起的正常调用:
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)
问题原因
当前规则的写法逻辑错误:.orShould()会将两个独立的onlyBeAccessed()规则组合成“SegmentDAO只能被SegmentHelper访问 OR SegmentDAO只能被SegmentHelperFake访问”的逻辑。但实际场景中两个类都在访问SegmentDAO,导致两个独立规则都不满足,因此触发违规。
修正后的规则
正确写法是将两个允许的类合并到同一个byClassesThat()条件中,用.or()连接匹配器,逻辑为“SegmentDAO只能被SegmentHelper或者SegmentHelperFake访问”:
写法一:使用类SimpleName匹配
@ArchTest public static final ArchRule daoShouldBeUsedOnlyByHelper = theClass(SegmentDAO.class).should() .onlyBeAccessed() .byClassesThat( simpleName(SegmentHelper.class.getSimpleName()) .or(simpleName(SegmentHelperFake.class.getSimpleName())) );
写法二:直接使用类对象匹配(更简洁)
@ArchTest public static final ArchRule daoShouldBeUsedOnlyByHelper = theClass(SegmentDAO.class).should() .onlyBeAccessed() .byClassesThat(are(SegmentHelper.class, SegmentHelperFake.class));
内容的提问来源于stack exchange,提问作者thomas113412
相关产品推荐
相关产品推荐

