Skip to content

Conversation

@DanielOber
Copy link
Contributor

@DanielOber DanielOber commented Sep 15, 2025

Pull Request

Changes

  • added arch unit tests for
    • naming of test classes
    • Naming of AfterEach Methods
    • Naming of BeforeEach Methods
    • Naming of Test cases
  • renamed tests cases because of new archunit rules

Reference

Issue: #758

Checklist

Note: If some checklist items are not relevant for your PR, just remove them.

General

  • I have read the Contribution Guidelines (TBD)
  • Met all acceptance criteria of the issue
  • Added meaningful PR title and list of changes in the description
  • Opened documentation issue in refarch repository (if applicable)
  • Opened follow-up issue in refarch repository (if applicable)

Code

  • [ ] Wrote code and comments in English
  • [ ] Added unit tests
  • Removed waste on branch (e.g. console.log), see code quality tooling

Backend / EAI

  • [ ] Added integration tests
  • [ ] Updated database migration scripts (if changes to model were made)
  • [ ] Added Swagger API annotations (if changes to API was made)
  • [ ] Checked Spring Boot version matching Camel version in pom.xml (if Camel version was bumped)

Screenshots (if UI was changed)

Summary by CodeRabbit

  • Tests

    • Added automated architecture tests enforcing Given-Then test naming, lifecycle names (setUp/tearDown), test visibility, and that test classes end with “Test”.
    • Renamed many test methods to a consistent Given-Then style.
    • Strengthened Cache-Control header verification with exact-value assertions.
  • Chores

    • Added ArchUnit support via a test-scoped dependency and a version property (test-only, no runtime impact).

@DanielOber DanielOber requested a review from a team as a code owner September 15, 2025 12:00
@DanielOber DanielOber linked an issue Sep 15, 2025 that may be closed by this pull request
2 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 15, 2025

Walkthrough

Adds ArchUnit as a test-scoped dependency and property; introduces an ArchUnit test harness, a Rules utility and a custom ArchCondition; renames many test methods to Given-Then style; adds an exact Cache-Control header assertion in one test.

Changes

Cohort / File(s) Summary
Build config
refarch-backend/pom.xml
Added archunit.version property (1.4.1) and test-scoped dependency com.tngtech.archunit:archunit using ${archunit.version}.
ArchUnit harness & rules
refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java, refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java, refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java
New parameterized ArchUnit test importing test classes and executing five ArchRule checks. Added Rules utility exposing five ArchRule constants and a custom ArchCondition<JavaMethod> enforcing top-level test class names end with "Test".
Filter tests — naming & header assertion
refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java, refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java
Renamed test methods to Given-Then style. In CacheControlFilterTest added assertion verifying exact Cache-Control header value ("no-cache, no-store, must-revalidate").
NFC converter & helper tests — renaming
refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java, refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java
Renamed multiple test methods to Given-Then style; test logic unchanged.
Security converters tests — renaming
refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java, refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java
Renamed several test methods to Given-Then style; no behavior changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant J as JUnit 5
  participant A as ArchUnitTest
  participant I as ClassFileImporter
  participant R as Rules

  J->>A: run parameterized tests
  A->>I: import tests (OnlyIncludeTests from app package)
  I-->>A: allTestClasses
  loop for each ArchRule
    A->>R: request ArchRule
    R-->>A: ArchRule
    A->>A: rule.check(allTestClasses)
  end
  A-->>J: report results (violations/success)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Pay attention to: Rules.java (rule definitions and static imports), TestClassesEndWithTestCondition.java (top-level enclosing-class traversal), and ArchUnitTest.java (import options and parameterization). Verify test renames did not break test discovery.

Poem

A rabbit hops through test files bright,
ArchUnit polishes names by starlight.
Given-then carrots line each trend,
Cache-control crisp — conventions mend.
🥕🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Added ArchUnit for better Consistency in Test-Classes' accurately and concisely summarizes the main change—introducing ArchUnit to enforce testing conventions.
Description check ✅ Passed The PR description follows the template with a clear 'Changes' section listing all four ArchUnit rules and the test renames, a valid Issue reference, and a completed checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 758-feature-archunit---validate-test-naming-convention---backend

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8910444 and a5fb268.

📒 Files selected for processing (1)
  • refarch-backend/pom.xml (2 hunks)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added Template: Backend Issues regarding the backend template. Type: Feature The issue is an feature labels Sep 15, 2025
@DanielOber DanielOber changed the title added archunit and fixed tests Added ArchUnit for better Consistency in Test-Classes Sep 15, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java (1)

47-52: Make Cache-Control assertion order-insensitive.

Some frameworks reorder directives or append extras (e.g., max-age=0). Assert presence instead of exact equality to avoid brittle tests.

-        assertEquals(EXPECTED_CACHE_CONTROL_HEADER_VALUES, response.getHeaders().getCacheControl());
+        final String cc = response.getHeaders().getCacheControl();
+        assertTrue(cc.contains("no-cache"));
+        assertTrue(cc.contains("no-store"));
+        assertTrue(cc.contains("must-revalidate"));
refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java (1)

61-81: Fix typo in test name and guard against NPEs from response/body lookup.

Current flow dereferences response before any null-check; repository lookup could also be null.

-    void givenDecomposedString_thenCovertToNfcNormalized() {
+    void givenDecomposedString_thenConvertToNfcNormalized() {
@@
-        final TheEntityResponseDTO response = testRestTemplate.postForEntity(URI.create(ENTITY_ENDPOINT_URL), theEntityRequestDto, TheEntityResponseDTO.class)
-                .getBody();
-        final TheEntity theEntity = theEntityRepository.findById(response.id()).orElse(null);
+        final TheEntityResponseDTO response = testRestTemplate
+                .postForEntity(URI.create(ENTITY_ENDPOINT_URL), theEntityRequestDto, TheEntityResponseDTO.class)
+                .getBody();
+        assertNotNull(response);
+        final TheEntity theEntity = theEntityRepository.findById(response.id()).orElse(null);
@@
-        assertNotNull(theEntity.getTextAttribute());
+        assertNotNull(theEntity);
+        assertNotNull(theEntity.getTextAttribute());
refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java (2)

75-96: Avoid cross-test coupling to NfcConverterTest.TOKEN.

Tests should not depend on constants from other test classes. Inline the value or extract a shared TestConstants.

-        assertEquals(NfcConverterTest.TOKEN, nfcCookie.getName());
+        assertEquals("token", nfcCookie.getName());
@@
-            assertEquals(NfcConverterTest.TOKEN, nfcCookie.getName());
+            assertEquals("token", nfcCookie.getName());

If desired, introduce a shared constant:

// src/test/java/de/muenchen/refarch/TestConstants.java
package de.muenchen.refarch;
public final class TestConstants {
  private TestConstants() {}
  public static final String TOKEN = "token";
}

31-59: Reduce duplication with parameterized tests.

The three near-identical assertions per input can be expressed via @ParameterizedTest with a source of NFD/NFC pairs for better maintainability.

refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java (2)

72-89: Nit: Prefer “ContentType” casing in method name.

Keeps names idiomatic and consistent with header term.

-    void givenContenttypeInWhitelist_thenFilter() throws ServletException, IOException {
+    void givenContentTypeInWhitelist_thenFilter() throws ServletException, IOException {

92-109: Nit: Prefer “ContentType” casing in method name.

-    void givenContenttypeNotInWhitelist_thenSkipFilter() throws ServletException, IOException {
+    void givenContentTypeNotInWhitelist_thenSkipFilter() throws ServletException, IOException {
refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java (1)

50-70: Test name contradicts behavior: it loads roles.

The method sets two authorities; rename to reflect “roles present”.

-    void givenNoRoles_thenConvert() {
+    void givenRoles_thenConvert() {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b2162fc and 6ac0961.

📒 Files selected for processing (10)
  • refarch-backend/pom.xml (2 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java (2 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java (5 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java (4 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)
  • TestClassesEndWithTestCondition (10-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Analyze Java source files (./refarch-eai)
  • GitHub Check: Analyze Java source files (./refarch-backend)
  • GitHub Check: Run docker compose healthcheck
  • GitHub Check: build (refarch-backend)
🔇 Additional comments (2)
refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java (1)

39-39: Renames align with the new Given-Then convention.

Method names match the ArchUnit regex and keep package‑private visibility. No further changes needed here.

Also applies to: 57-57, 74-74, 90-90

refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1)

24-31: Anchor lifecycle method names.

Without ^...$, names like setUpData or tearDownNow would pass.

Apply this diff:

-    public static final ArchRule RULE_BEFORE_EACH_NAMING_CONVENTION_MATCHED = methods()
-            .that().areAnnotatedWith(BeforeEach.class).should().haveNameMatching("setUp")
+    public static final ArchRule RULE_BEFORE_EACH_NAMING_CONVENTION_MATCHED = methods()
+            .that().areAnnotatedWith(BeforeEach.class).should().haveNameMatching("^setUp$")
             .allowEmptyShould(true);

-    public static final ArchRule RULE_AFTER_EACH_NAMING_CONVENTION_MATCHED = methods()
-            .that().areAnnotatedWith(AfterEach.class).should().haveNameMatching("tearDown")
+    public static final ArchRule RULE_AFTER_EACH_NAMING_CONVENTION_MATCHED = methods()
+            .that().areAnnotatedWith(AfterEach.class).should().haveNameMatching("^tearDown$")
             .allowEmptyShould(true);

Likely an incorrect or invalid review comment.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (2)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (2)

19-22: Tighten the test-name regex: use a single end anchor.

Multiple $ are redundant.

-            .should().haveNameMatching("^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$$$$$");
+            .should().haveNameMatching("^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$");

16-17: Make this utility holder class final.

Prevents subclassing; you already block instantiation with the private no‑args ctor.

-@NoArgsConstructor(access = AccessLevel.PRIVATE)
-public class MethodRules {
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class MethodRules {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6ac0961 and 8bcf652.

📒 Files selected for processing (1)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)
  • TestClassesEndWithTestCondition (10-37)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (6)
refarch-backend/pom.xml (2)

61-61: Pin looks fine; confirm compatibility with JDK 21/JUnit 5.

1.4.1 should be OK, but please confirm locally to avoid CI surprises.

#!/bin/bash
# Verify we don't rely on JUnit 5 integration classes (which would need archunit-junit5*)
rg -nP 'com\.tngtech\.archunit\.junit5' -C2 --type=java || true

263-268: Consider junit5 integration artifact; keep test scope.

Scope is correct. If you ever use ArchUnit’s JUnit 5 annotations/extensions, switch to the junit5 engine artifact.

-        <dependency>
-            <groupId>com.tngtech.archunit</groupId>
-            <artifactId>archunit</artifactId>
-            <version>${archunit.version}</version>
-            <scope>test</scope>
-        </dependency>
+        <dependency>
+            <groupId>com.tngtech.archunit</groupId>
+            <artifactId>archunit-junit5-engine</artifactId>
+            <version>${archunit.version}</version>
+            <scope>test</scope>
+        </dependency>
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)

21-24: Optional: allow common suffixes “Tests”/“IT”.

Broaden the rule to typical test class suffixes.

-        if (!topEnclosingClass.getSimpleName().endsWith("Test")) {
+        if (!allowedTestSuffix(topEnclosingClass.getSimpleName())) {
             events.add(SimpleConditionEvent.violated(method, "Method %s must be declared in a class whose simple name ends with 'Test' (found: %s)"
                     .formatted(method.getName(), topEnclosingClass.getSimpleName())));
         }
     }
 
     private JavaClass getTopEnclosingClass(JavaClass item) {
         while (item.getEnclosingClass().isPresent()) {
             item = item.getEnclosingClass().orElseThrow();
         }
         return item;
     }
+
+    private static boolean allowedTestSuffix(String simpleName) {
+        return simpleName.endsWith("Test") || simpleName.endsWith("Tests") || simpleName.endsWith("IT");
+    }

Also applies to: 27-32

refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (3)

23-29: Anchor exact method names for BeforeEach/AfterEach.

Avoid substring matches; use exact name.

-    public static final ArchRule RULE_BEFORE_EACH_NAMING_CONVENTION_MATCHED = methods()
-            .that().areAnnotatedWith(BeforeEach.class).should().haveNameMatching("setUp")
+    public static final ArchRule RULE_BEFORE_EACH_NAMING_CONVENTION_MATCHED = methods()
+            .that().areAnnotatedWith(BeforeEach.class).should().haveNameMatching("^setUp$")
             .allowEmptyShould(true);
 
-    public static final ArchRule RULE_AFTER_EACH_NAMING_CONVENTION_MATCHED = methods()
-            .that().areAnnotatedWith(AfterEach.class).should().haveNameMatching("tearDown")
+    public static final ArchRule RULE_AFTER_EACH_NAMING_CONVENTION_MATCHED = methods()
+            .that().areAnnotatedWith(AfterEach.class).should().haveNameMatching("^tearDown$")
             .allowEmptyShould(true);

31-35: Allow empty result to avoid false negatives in modules without tests.

Align with the other rules.

     public static final ArchRule RULE_TEST_METHODS_ARE_PACKAGE_PRIVATE_CONVENTION_MATCHED = methods()
             .that().areAnnotatedWith(Test.class).or().areAnnotatedWith(ParameterizedTest.class).should()
             .notHaveModifier(JavaModifier.PROTECTED)
             .andShould().notHaveModifier(JavaModifier.PRIVATE)
-            .andShould().notHaveModifier(JavaModifier.PUBLIC);
+            .andShould().notHaveModifier(JavaModifier.PUBLIC)
+            .allowEmptyShould(true);

37-39: Consider allowEmptyShould(true) for consistency.

Keeps behavior consistent across rules.

     public static final ArchRule RULE_TESTCLASSES_END_WITH_TEST_CONVENTION_MATCHED = methods()
             .that().areAnnotatedWith(Test.class).or().areAnnotatedWith(ParameterizedTest.class)
-            .should(haveTopEnclosingClassEndingWithTest);
+            .should(haveTopEnclosingClassEndingWithTest)
+            .allowEmptyShould(true);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8bcf652 and 41d5b5c.

📒 Files selected for processing (3)
  • refarch-backend/pom.xml (2 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)
  • TestClassesEndWithTestCondition (9-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze Java source files (./refarch-eai)
  • GitHub Check: Analyze Java source files (./refarch-backend)
  • GitHub Check: build (refarch-backend)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (7)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (2)

18-25: Allow “Tests”/“IT” suffixes and reflect them in the violation message.

Current rule is too strict for common integration-test conventions.

-        if (!topEnclosingClass.getSimpleName().endsWith("Test")) {
-            events.add(SimpleConditionEvent.violated(method, "Method %s must be declared in a class whose simple name ends with 'Test' (found: %s)"
-                    .formatted(method.getName(), topEnclosingClass.getSimpleName())));
+        if (!allowedTestSuffix(topEnclosingClass.getSimpleName())) {
+            events.add(SimpleConditionEvent.violated(
+                method,
+                "Method %s must be declared in a class whose simple name ends with one of [Test, Tests, IT] (found: %s)"
+                    .formatted(method.getName(), topEnclosingClass.getSimpleName())
+            ));
         }

Add alongside this class (outside the diff range):

// imports:
// import java.util.Set;

private static boolean allowedTestSuffix(String simpleName) {
    return simpleName.endsWith("Test") || simpleName.endsWith("Tests") || simpleName.endsWith("IT");
}

11-13: Align condition description with allowed suffixes.

If you adopt multiple suffixes, update the label for clarity.

-        super("have top enclosing class name ending with `Test`");
+        super("have top enclosing class name ending with one of [`Test`, `Tests`, `IT`]");
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (5)

18-21: Allow empty result to avoid false negatives in modules without tests.

     public static final ArchRule RULE_TEST_NAMING_CONVENTION_SHOULD_WHEN_MATCHED = methods()
             .that().areAnnotatedWith(Test.class).or().areAnnotatedWith(ParameterizedTest.class)
-            .should().haveNameMatching("^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$");
+            .should().haveNameMatching("^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$")
+            .allowEmptyShould(true);

22-25: Anchor exact BeforeEach method name.

-            .that().areAnnotatedWith(BeforeEach.class).should().haveNameMatching("setUp")
+            .that().areAnnotatedWith(BeforeEach.class).should().haveNameMatching("^setUp$")

26-29: Anchor exact AfterEach method name.

-            .that().areAnnotatedWith(AfterEach.class).should().haveNameMatching("tearDown")
+            .that().areAnnotatedWith(AfterEach.class).should().haveNameMatching("^tearDown$")

30-35: Permit empty result and keep rules consistent.

     public static final ArchRule RULE_TEST_METHODS_ARE_PACKAGE_PRIVATE_CONVENTION_MATCHED = methods()
             .that().areAnnotatedWith(Test.class).or().areAnnotatedWith(ParameterizedTest.class).should()
             .notHaveModifier(JavaModifier.PROTECTED)
             .andShould().notHaveModifier(JavaModifier.PRIVATE)
-            .andShould().notHaveModifier(JavaModifier.PUBLIC);
+            .andShould().notHaveModifier(JavaModifier.PUBLIC)
+            .allowEmptyShould(true);

36-39: Also allow empty result for the “classes end with Test” rule.

     public static final ArchRule RULE_TESTCLASSES_END_WITH_TEST_CONVENTION_MATCHED = methods()
             .that().areAnnotatedWith(Test.class).or().areAnnotatedWith(ParameterizedTest.class)
-            .should(haveTopEnclosingClassEndingWithTest);
+            .should(haveTopEnclosingClassEndingWithTest)
+            .allowEmptyShould(true);
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 41d5b5c and 6ba6a2b.

📒 Files selected for processing (2)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)
  • TestClassesEndWithTestCondition (9-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze Java source files (./refarch-backend)
  • GitHub Check: Analyze Java source files (./refarch-eai)
  • GitHub Check: build (refarch-backend)
🔇 Additional comments (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/MethodRules.java (1)

15-16: Utility holder hardened correctly.

final + private ctor via Lombok is appropriate.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java (1)

26-30: Prefer strongly typed parameters over ArgumentsAccessor (repeat).

Keeps the display name behavior and adds compile-time safety.

Apply:

-    @ParameterizedTest(name = "{0}")
-    @MethodSource("allTestClassesRulesToVerify")
-    void givenAllArchUnitRulesForAllTestClasses_thenRunArchUnitTests(final ArgumentsAccessor arguments) {
-        arguments.get(1, ArchRule.class).check(allTestClasses);
-    }
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("allTestClassesRulesToVerify")
+    void givenAllArchUnitRulesForAllTestClasses_thenRunArchUnitTests(final String ruleName, final ArchRule rule) {
+        rule.check(allTestClasses);
+    }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 429c843 and 8988021.

📒 Files selected for processing (2)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java (1 hunks)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)
  • TestClassesEndWithTestCondition (9-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze Java source files (./refarch-eai)
  • GitHub Check: Analyze Java source files (./refarch-backend)
  • GitHub Check: build (refarch-backend)
🔇 Additional comments (2)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java (1)

1-24: Overall ArchUnit setup looks good.

Scoped import of test classes and one-time initialization are sensible. No blocking issues here.

refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java (1)

15-39: Rules set is clear and cohesive.

Good use of ArchUnit DSL; sensible allowEmptyShould for lifecycle hooks.

Copy link
Member

@devtobi devtobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am starting to wonder if the configured rules (e.g. modifiers for test methods, naming of test methods, etc.) could also be implemented via different mechanisms like linting tools.
Enforcing best practices and uniform implementations increases readability and maintainability, on the other hand its more nitpicky and could annoy users.

@DanielOber DanielOber requested a review from devtobi October 10, 2025 11:34
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8988021 and 8cc94f3.

📒 Files selected for processing (1)
  • refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java (1)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java (1)
  • TestClassesEndWithTestCondition (9-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Analyze Java source files (./refarch-eai)
  • GitHub Check: Analyze Java source files (./refarch-backend)
  • GitHub Check: build (refarch-backend)
🔇 Additional comments (5)
refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java (5)

17-18: LGTM: Proper utility class structure.

The combination of final class and @NoArgsConstructor(access = PRIVATE) correctly prevents instantiation and subclassing.


20-26: Verify the regex pattern strictness.

The current pattern ^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$ requires at least two characters after "given" and "then" (e.g., givenXx_thenYy). This means method names like givenNull_thenOk would pass, but givenX_thenY would fail.

Is this intentional? If single-letter suffixes should be allowed, consider:

-            .should().haveNameMatching("^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$");
+            .should().haveNameMatching("^given[A-Z][a-zA-Z]*_then[A-Z][a-zA-Z]*$");

This changes + (one or more) to * (zero or more), allowing givenX_thenY while still requiring the initial uppercase letter.


28-34: LGTM: Lifecycle method rules properly anchored.

The regex patterns for setUp and tearDown are correctly anchored (^setUp$ and ^tearDown$), addressing the previous review feedback. The use of allowEmptyShould(true) appropriately handles cases where no such methods exist.

Based on learnings


36-43: LGTM: Package-private enforcement logic is correct.

The rule correctly enforces package-private (default) access by prohibiting PROTECTED, PRIVATE, and PUBLIC modifiers on test methods.


45-50: LGTM: Test class naming rule uses custom condition correctly.

The rule properly leverages the haveTopEnclosingClassEndingWithTest custom condition to enforce that test methods reside in classes ending with "Test", handling nested classes correctly.

Comment on lines +34 to +39
Arguments.of("RULE_TESTCLASSES_END_WITH_TEST_CONVENTION_MATCHED",
Rules.RULE_TESTCLASSES_END_WITH_TEST_CONVENTION_MATCHED),
Arguments.of("TEST_NAMING_CONVENTION_RULE", Rules.RULE_TEST_NAMING_CONVENTION_GIVEN_THEN_MATCHED),
Arguments.of("RULE_BEFORE_EACH_NAMING_CONVENTION_MATCHED", Rules.RULE_BEFORE_EACH_NAMING_CONVENTION_MATCHED),
Arguments.of("RULE_AFTER_EACH_NAMING_CONVENTION_MATCHED", Rules.RULE_AFTER_EACH_NAMING_CONVENTION_MATCHED),
Arguments.of("TEST_METHODS_ARE_PACKAGE_PRIVATE_CONVENTION_MATCHED", Rules.RULE_TEST_METHODS_ARE_PACKAGE_PRIVATE_CONVENTION_MATCHED));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of duplicating the rule name I'd add a more descriptive name that describes the ArchUnit check

arguments.get(1, ArchRule.class).check(allTestClasses);
}

public static Stream<Arguments> allTestClassesRulesToVerify() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd move this method all the way to the top because when we add a new rule this stream needs to be extended but the parameterized itself not so its less important.

Comment on lines +21 to +25
.that().areAnnotatedWith(Test.class)
.or().areAnnotatedWith(ParameterizedTest.class)
.or().areAnnotatedWith(RepeatedTest.class)
.or()
.areAnnotatedWith(TestTemplate.class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most tests have these 4 lines in common, maybe you can move them into a reusable snippet? e.g. areTests()

Comment on lines +37 to +40
.that().areAnnotatedWith(Test.class)
.or().areAnnotatedWith(ParameterizedTest.class)
.or().areAnnotatedWith(RepeatedTest.class).or()
.areAnnotatedWith(TestTemplate.class).should()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code duplication

Comment on lines +46 to +49
.that().areAnnotatedWith(Test.class)
.or().areAnnotatedWith(ParameterizedTest.class)
.or().areAnnotatedWith(RepeatedTest.class).or()
.areAnnotatedWith(TestTemplate.class)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code duplication

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Template: Backend Issues regarding the backend template. Type: Feature The issue is an feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] ArchUnit - validate test naming convention - backend

3 participants