diff --git a/refarch-backend/pom.xml b/refarch-backend/pom.xml
index 0dd0cfbc3..3546ab434 100644
--- a/refarch-backend/pom.xml
+++ b/refarch-backend/pom.xml
@@ -58,6 +58,7 @@
0.8.14
+ 1.4.1
3.2.0
@@ -259,6 +260,12 @@
spring-boot-configuration-processor
true
+
+ com.tngtech.archunit
+ archunit
+ ${archunit.version}
+ test
+
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java
new file mode 100644
index 000000000..88c63b195
--- /dev/null
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/archunit/ArchUnitTest.java
@@ -0,0 +1,42 @@
+package de.muenchen.refarch.archunit;
+
+import com.tngtech.archunit.core.domain.JavaClasses;
+import com.tngtech.archunit.core.importer.ClassFileImporter;
+import com.tngtech.archunit.core.importer.ImportOption;
+import com.tngtech.archunit.lang.ArchRule;
+import de.muenchen.refarch.archunit.rules.Rules;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.aggregator.ArgumentsAccessor;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class ArchUnitTest {
+
+ private static JavaClasses allTestClasses;
+
+ @BeforeAll
+ static void init() {
+ allTestClasses = new ClassFileImporter()
+ .withImportOption(new ImportOption.OnlyIncludeTests())
+ .importPackages(de.muenchen.refarch.MicroServiceApplication.class.getPackage().getName());
+ }
+
+ @ParameterizedTest(name = "{0}")
+ @MethodSource("allTestClassesRulesToVerify")
+ void givenAllArchUnitRulesForAllTestClasses_thenRunArchUnitTests(final ArgumentsAccessor arguments) {
+ arguments.get(1, ArchRule.class).check(allTestClasses);
+ }
+
+ public static Stream allTestClassesRulesToVerify() {
+ return Stream.of(
+ 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));
+ }
+
+}
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java b/refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java
new file mode 100644
index 000000000..713b9fb6b
--- /dev/null
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/Rules.java
@@ -0,0 +1,51 @@
+package de.muenchen.refarch.archunit.rules;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;
+import static de.muenchen.refarch.archunit.rules.TestClassesEndWithTestCondition.haveTopEnclosingClassEndingWithTest;
+
+import com.tngtech.archunit.core.domain.JavaModifier;
+import com.tngtech.archunit.lang.ArchRule;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.RepeatedTest;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestTemplate;
+import org.junit.jupiter.params.ParameterizedTest;
+
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class Rules {
+
+ public static final ArchRule RULE_TEST_NAMING_CONVENTION_GIVEN_THEN_MATCHED = methods()
+ .that().areAnnotatedWith(Test.class)
+ .or().areAnnotatedWith(ParameterizedTest.class)
+ .or().areAnnotatedWith(RepeatedTest.class)
+ .or()
+ .areAnnotatedWith(TestTemplate.class)
+ .should().haveNameMatching("^given[A-Z][a-zA-Z]+_then[A-Z][a-zA-Z]+$");
+
+ 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$")
+ .allowEmptyShould(true);
+
+ public static final ArchRule RULE_TEST_METHODS_ARE_PACKAGE_PRIVATE_CONVENTION_MATCHED = methods()
+ .that().areAnnotatedWith(Test.class)
+ .or().areAnnotatedWith(ParameterizedTest.class)
+ .or().areAnnotatedWith(RepeatedTest.class).or()
+ .areAnnotatedWith(TestTemplate.class).should()
+ .notHaveModifier(JavaModifier.PROTECTED)
+ .andShould().notHaveModifier(JavaModifier.PRIVATE)
+ .andShould().notHaveModifier(JavaModifier.PUBLIC);
+
+ public static final ArchRule RULE_TESTCLASSES_END_WITH_TEST_CONVENTION_MATCHED = methods()
+ .that().areAnnotatedWith(Test.class)
+ .or().areAnnotatedWith(ParameterizedTest.class)
+ .or().areAnnotatedWith(RepeatedTest.class).or()
+ .areAnnotatedWith(TestTemplate.class)
+ .should(haveTopEnclosingClassEndingWithTest);
+}
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java b/refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java
new file mode 100644
index 000000000..a6975fdfb
--- /dev/null
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/archunit/rules/TestClassesEndWithTestCondition.java
@@ -0,0 +1,33 @@
+package de.muenchen.refarch.archunit.rules;
+
+import com.tngtech.archunit.core.domain.JavaClass;
+import com.tngtech.archunit.core.domain.JavaMethod;
+import com.tngtech.archunit.lang.ArchCondition;
+import com.tngtech.archunit.lang.ConditionEvents;
+import com.tngtech.archunit.lang.SimpleConditionEvent;
+
+public class TestClassesEndWithTestCondition extends ArchCondition {
+
+ TestClassesEndWithTestCondition() {
+ super("have top enclosing class name ending with `Test`");
+ }
+
+ public static final ArchCondition haveTopEnclosingClassEndingWithTest = new TestClassesEndWithTestCondition();
+
+ @Override
+ public void check(JavaMethod method, ConditionEvents events) {
+ final var topEnclosingClass = getTopEnclosingClass(method.getOwner());
+
+ 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())));
+ }
+ }
+
+ private JavaClass getTopEnclosingClass(JavaClass item) {
+ while (item.getEnclosingClass().isPresent()) {
+ item = item.getEnclosingClass().orElseThrow();
+ }
+ return item;
+ }
+}
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java
index fad824e4d..9c0bef952 100644
--- a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/CacheControlFilterTest.java
@@ -44,7 +44,7 @@ class CacheControlFilterTest {
private TestRestTemplate testRestTemplate;
@Test
- void testForCacheControlHeadersForEntityEndpoint() {
+ void givenEntityEndpoint_thenCacheControlHeadersPresent() {
final ResponseEntity response = testRestTemplate.exchange(ENTITY_ENDPOINT_URL, HttpMethod.GET, null, String.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertTrue(response.getHeaders().containsKey(HttpHeaders.CACHE_CONTROL));
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java
index ba3cea914..fb7fd0b4e 100644
--- a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/UnicodeFilterConfigurationTest.java
@@ -58,7 +58,7 @@ class UnicodeFilterConfigurationTest {
private TheEntityRepository theEntityRepository;
@Test
- void testForNfcNormalization() {
+ void givenDecomposedString_thenCovertToNfcNormalized() {
// Given
// Persist entity with decomposed string.
final TheEntityRequestDTO theEntityRequestDto = new TheEntityRequestDTO(TEXT_ATTRIBUTE_DECOMPOSED);
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java
index e0ec0a0d4..5025bcff6 100644
--- a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcConverterTest.java
@@ -70,7 +70,7 @@ class NfcConverterTest {
// Test that request with configured ContentType is normalized to NFC.
@Test
- void testFilterIfContenttypeInWhitelist() throws ServletException, IOException {
+ void givenContenttypeInWhitelist_thenFilter() throws ServletException, IOException {
mockRequest("text/plain");
filter.doFilter(req, resp, chain);
@@ -90,7 +90,7 @@ void testFilterIfContenttypeInWhitelist() throws ServletException, IOException {
// Test that Request not configured ContentType remains unchanged, i.e. is not normalized to NFC.
@Test
- void testSkipFilterIfContenttypeNotInWhitelist() throws ServletException, IOException {
+ void givenContenttypeNotInWhitelist_thenSkipFilter() throws ServletException, IOException {
mockRequest("application/notvalid");
filter.doFilter(req, resp, chain);
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java
index 9c13e92a5..22aa1d327 100644
--- a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/filter/nfcconverter/NfcHelperTest.java
@@ -29,7 +29,7 @@ class NfcHelperTest {
private static final String[] NFC_OUTPUT_EXPECTED = { FIRST_NFC, SECOND_NFC, THIRD_NFC };
@Test
- void nfcConverterString() {
+ void givenString_thenNfcConverted() {
assertEquals(FIRST_NFC, NfcHelper.nfcConverter(FIRST_NFD));
assertEquals(FIRST_NFC.length(), NfcHelper.nfcConverter(FIRST_NFD).length());
@@ -41,7 +41,7 @@ void nfcConverterString() {
}
@Test
- void nfcConverterStringBuffer() {
+ void givenStringBuffer_thenNfcConverted() {
assertEquals(FIRST_NFC, NfcHelper.nfcConverter(new StringBuffer(FIRST_NFD)).toString());
assertEquals(FIRST_NFC.length(), NfcHelper.nfcConverter(new StringBuffer(FIRST_NFD)).length());
@@ -53,13 +53,13 @@ void nfcConverterStringBuffer() {
}
@Test
- void nfcConverterStringArray() {
+ void givenStringArray_thenNfcConverted() {
assertArrayEquals(NFC_OUTPUT_EXPECTED, NfcHelper.nfcConverter(NFD_INPUT));
assertEquals(NFC_OUTPUT_EXPECTED.length, NfcHelper.nfcConverter(NFD_INPUT).length);
}
@Test
- void nfcConverterMapOfStrings() {
+ void givenMapOfStrings_thenNfcConverted() {
final Map nfdInput = new HashMap<>();
nfdInput.put(FIRST_NFD, NFD_INPUT);
nfdInput.put(SECOND_NFD, NFD_INPUT);
@@ -73,7 +73,7 @@ void nfcConverterMapOfStrings() {
}
@Test
- void nfcConverterCookie() {
+ void givenCookie_thenNfcConverted() {
final Cookie nfcCookie = NfcHelper.nfcConverter(createNfdCookie());
assertEquals(NfcConverterTest.TOKEN, nfcCookie.getName());
@@ -84,7 +84,7 @@ void nfcConverterCookie() {
@SuppressWarnings("PMD.UnitTestShouldIncludeAssert")
@Test
- void nfcConverterCookieArray() {
+ void givenCookieArray_thenNfcConverted() {
final Cookie[] nfdCookies = Collections.nCopies(3, createNfdCookie()).toArray(new Cookie[3]);
final Cookie[] nfcCookies = NfcHelper.nfcConverter(nfdCookies);
Arrays.asList(nfcCookies).forEach(nfcCookie -> {
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java
index a3a00ba75..5ec646282 100644
--- a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/KeycloakRolesAuthoritiesConverterTest.java
@@ -36,7 +36,7 @@ void setUp() {
}
@Test
- void testConvert_WithRoles() {
+ void givenRoles_thenConvert() {
// Setup
final Map resourceAccessClaim = new HashMap<>();
resourceAccessClaim.put(TEST_CLIENT, Map.of("roles", List.of("admin", "user")));
@@ -54,7 +54,7 @@ void testConvert_WithRoles() {
}
@Test
- void testConvert_WithoutRoles() {
+ void givenNoRoles_thenConvert() {
// Setup
final Map claims = new HashMap<>();
claims.put(RESOURCE_ACCESS_CLAIM, Map.of(
@@ -71,7 +71,7 @@ void testConvert_WithoutRoles() {
}
@Test
- void testConvert_ClientNotInResourceAccess() {
+ void givenClientNotInResourceAccess_thenConvert() {
// Setup
final Map resourceAccessClaim = new HashMap<>();
resourceAccessClaim.put("other-client", Map.of("roles", List.of("admin")));
@@ -87,7 +87,7 @@ void testConvert_ClientNotInResourceAccess() {
}
@Test
- void testConvert_NullClaims() {
+ void givenNullClaims_thenConvert() {
// Setup
final Jwt jwt = mock(Jwt.class);
diff --git a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java
index fb718fdb4..11f0e7c47 100644
--- a/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java
+++ b/refarch-backend/src/test/java/de/muenchen/refarch/configuration/security/UserInfoAuthoritiesConverterTest.java
@@ -48,7 +48,7 @@ void setUp() {
}
@Test
- void testConvert_WithAuthorities() {
+ void givenNoRoles_thenConvert() {
// Setup
final Jwt jwt = mock(Jwt.class);
when(jwt.getSubject()).thenReturn(TEST_SUBJECT);
@@ -70,7 +70,7 @@ void testConvert_WithAuthorities() {
}
@Test
- void testConvert_NoAuthorities() {
+ void givenNoAuthorities_thenConvert() {
// Setup
final Jwt jwt = mock(Jwt.class);
when(jwt.getSubject()).thenReturn(TEST_SUBJECT);
@@ -89,7 +89,7 @@ void testConvert_NoAuthorities() {
}
@Test
- void testConvert_CacheHit() {
+ void givenCacheHit_thenConvert() {
// Setup
final Jwt jwt = mock(Jwt.class);
when(jwt.getSubject()).thenReturn(TEST_SUBJECT);