From 4ccfef8ec0e93011b1f779b2aa143dc96424f2bf Mon Sep 17 00:00:00 2001 From: Ulli Hafner Date: Wed, 3 Jun 2026 21:02:12 +0200 Subject: [PATCH] Add a rule to check for non-private instance fields --- doc/uml/class-diagram-technical.puml | 2 +- .../hm/hafner/archunit/ArchitectureRules.java | 5 +++++ .../archunit/ArchitectureRulesTest.java | 19 ++++++++++++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/uml/class-diagram-technical.puml b/doc/uml/class-diagram-technical.puml index 037d8b2bb..9f4875e25 100644 --- a/doc/uml/class-diagram-technical.puml +++ b/doc/uml/class-diagram-technical.puml @@ -90,7 +90,7 @@ note "Klasse" as Class note "Interface" as Interface note "Vererbung" as Vererbung note "Implementierung" as Implementierung -note "gerichtete Assoziation" as Assoziation +note "gerichtete Assoziation\n als Objektvariable" as Assoziation note "Vererbung und dabei\ngenerischen Typ binden" as Generics note "gerichtete Abhängigkeit:\n<> benutzt\n<> erzeugt\n<> Aufruf" as Dependency note "Aggregation" as Aggregation diff --git a/src/test/java/edu/hm/hafner/archunit/ArchitectureRules.java b/src/test/java/edu/hm/hafner/archunit/ArchitectureRules.java index 4f6e48051..0464ee600 100644 --- a/src/test/java/edu/hm/hafner/archunit/ArchitectureRules.java +++ b/src/test/java/edu/hm/hafner/archunit/ArchitectureRules.java @@ -32,6 +32,11 @@ * @author Ullrich Hafner */ public final class ArchitectureRules { + /** No class should have non-private instance fields. */ + public static final ArchRule ONLY_PRIVATE_FIELDS = + fields().that().doNotHaveModifier(JavaModifier.STATIC) + .should().bePrivate().allowEmptyShould(true); + /** Tests should not use fields. Recommendation is to use factory methods for stubs and mocks. */ public static final ArchRule NO_FIELDS_IN_TESTS = fields().that().areDeclaredInClassesThat().haveSimpleNameEndingWith("Test") diff --git a/src/test/java/edu/hm/hafner/archunit/ArchitectureRulesTest.java b/src/test/java/edu/hm/hafner/archunit/ArchitectureRulesTest.java index b85fb05d7..c8dd74d1f 100644 --- a/src/test/java/edu/hm/hafner/archunit/ArchitectureRulesTest.java +++ b/src/test/java/edu/hm/hafner/archunit/ArchitectureRulesTest.java @@ -21,6 +21,16 @@ class ArchitectureRulesTest { private static final String BROKEN_CLASS_NAME = ArchitectureRulesViolatedTest.class.getTypeName(); + @Test + void shouldVerifyThatFieldsArePrivate() { + assertThatExceptionOfType(AssertionError.class).isThrownBy( + () -> ArchitectureRules.ONLY_PRIVATE_FIELDS.check(importBrokenClass())) + .withMessageContainingAll(BROKEN_CLASS_NAME, "fields that do not have modifier STATIC should be private' was violated"); + + assertThatNoException().isThrownBy( + () -> ArchitectureRules.ONLY_PRIVATE_FIELDS.check(importPassingClass())); + } + @Test void shouldUseProtectedForReadResolve() { assertThatExceptionOfType(AssertionError.class).isThrownBy( @@ -100,7 +110,7 @@ void shouldVerifyNoPublicTestMethodsRule() { private JavaClasses importPassingClass() { return new ClassFileImporter().importClasses(ArchitectureRulesPassedTest.class, - ArchitectureRulesAlsoPassedTest.class); + ArchitectureRulesAlsoPassedTest.class, ArchitectureRulesPassed.class); } private JavaClasses importBrokenClass() { @@ -117,6 +127,8 @@ public static class ArchitectureRulesViolatedTest { @edu.umd.cs.findbugs.annotations.Nullable private final String noNullable = null; + int nonPrivate; + @Test @Disabled("This test is just there to be used in architecture tests") public void shouldFail() { org.junit.jupiter.api.Assertions.assertEquals(1, 1); @@ -188,4 +200,9 @@ protected Object readResolve() { return this; } } + + @SuppressWarnings("all") // This class is just there to be used in architecture tests + static class ArchitectureRulesPassed { + private int privateField; + } }