From 20468a0bee6e951e644ec91dcacb5a66d382bb11 Mon Sep 17 00:00:00 2001 From: mnhock Date: Sun, 16 Jun 2024 21:22:42 +0200 Subject: [PATCH] Provide a new JUnit 5 Rule checking test classes are package private Closes gh-28 --- docs/USERGUIDE.md | 21 +++++++++++++++---- .../enofex/taikai/test/JUnit5Configurer.java | 13 ++++++++++++ .../com/enofex/taikai/ArchitectureTest.java | 11 +++++----- src/test/java/com/enofex/taikai/Usage.java | 17 +++++++-------- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/docs/USERGUIDE.md b/docs/USERGUIDE.md index 6978823..3d6bc3c 100644 --- a/docs/USERGUIDE.md +++ b/docs/USERGUIDE.md @@ -23,7 +23,7 @@ Architecture rules are defined using Taikai's fluent API, allowing developers to |------------|----------------|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|-------------------------| | **Java** | General | `classesShouldImplementHashCodeAndEquals` | Classes should implement `hashCode` and `equals` | Default (WITHOUT_TESTS) | | | General | `fieldsShouldNotBePublic` | Fields should not be `public` (except constants) | Default (WITHOUT_TESTS) | -| | General | `methodsShouldNotDeclareGenericExceptions` | Methods should not declare generic exceptions (`Exception`, `RuntimeException`) | Default (WITHOUT_TESTS) | +| | General | `methodsShouldNotDeclareGenericExceptions` | Methods should not declare generic exceptions (`Exception`, `RuntimeException`) | Default (WITHOUT_TESTS) | | | General | `noUsageOf` | Disallow usage of specific classes | Default (WITHOUT_TESTS) | | | General | `noUsageOf` | Disallow usage of specific classes by class reference | Default (WITHOUT_TESTS) | | | General | `noUsageOfDeprecatedAPIs` | No usage of deprecated APIs annotated with `Deprecated` | Default (WITHOUT_TESTS) | @@ -40,9 +40,10 @@ Architecture rules are defined using Taikai's fluent API, allowing developers to | | Naming | `fieldsAnnotatedWithShouldMatch` | Fields annotated with should match specific naming patterns | Default (WITHOUT_TESTS) | | | Naming | `constantsShouldFollowConvention` | Constants should follow naming conventions | Default (WITHOUT_TESTS) | | | Naming | `interfacesShouldNotHavePrefixI` | Interfaces should not have the prefix `I` | Default (WITHOUT_TESTS) | -| **Test** | JUnit 5 | `classesShouldNotBeAnnotatedWithDisabled` | Ensure classes are not annotated with `@Disabled` | Default (ONLY_TESTS) | -| | JUnit 5 | `methodsShouldNotBeAnnotatedWithDisabled` | Ensure methods are not annotated with `@Disabled` | Default (ONLY_TESTS) | +| **Test** | JUnit 5 | `classesShouldBePackagePrivate` | Ensure that classes whose names match a specific naming pattern are declared as package-private. | Default (ONLY_TESTS) | +| | JUnit 5 | `classesShouldNotBeAnnotatedWithDisabled` | Ensure classes are not annotated with `@Disabled` | Default (ONLY_TESTS) | | | JUnit 5 | `methodsShouldBePackagePrivate` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` are package-private. | Default (ONLY_TESTS) | +| | JUnit 5 | `methodsShouldNotBeAnnotatedWithDisabled` | Ensure methods are not annotated with `@Disabled` | Default (ONLY_TESTS) | | | JUnit 5 | `methodsShouldBeAnnotatedWithDisplayName` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` are annotated with `@DisplayName`. | Default (ONLY_TESTS) | | | JUnit 5 | `methodsShouldMatch` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` have names matching a specific regex pattern. | Default (ONLY_TESTS) | | | JUnit 5 | `methodsShouldNotDeclareExceptions` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` do not declare any thrown exceptions. | Default (ONLY_TESTS) | @@ -251,7 +252,7 @@ Taikai.builder() .check(); ``` -- **Ensure Test Methods Do Not Declare Thrown Exceptions** Ensure that JUnit 5 test methods annotated with `@Test` or `@ParameterizedTest` do not declare any thrown exceptions. +- **Ensure Test Methods Do Not Declare Thrown Exceptions**: Ensure that JUnit 5 test methods annotated with `@Test` or `@ParameterizedTest` do not declare any thrown exceptions. ```java Taikai.builder() @@ -263,6 +264,18 @@ Taikai.builder() .check(); ``` +- **Ensure Classes with Matching Names are Package-Private**: Ensure that classes whose names match a specified regex pattern are declared as package-private. + +```java +Taikai.builder() + .namespace("com.company.yourproject") + .test(test -> test + .junit5(junit5 -> junit5 + .classesShouldBePackagePrivate(".*Test")) + .build() + .check(); +``` + ### Spring Configuration Spring configuration involves defining constraints specific to Spring Framework usage. diff --git a/src/main/java/com/enofex/taikai/test/JUnit5Configurer.java b/src/main/java/com/enofex/taikai/test/JUnit5Configurer.java index 12cee41..1562252 100644 --- a/src/main/java/com/enofex/taikai/test/JUnit5Configurer.java +++ b/src/main/java/com/enofex/taikai/test/JUnit5Configurer.java @@ -7,6 +7,7 @@ import static com.enofex.taikai.test.JUnit5Predicates.ANNOTATION_TEST; import static com.enofex.taikai.test.JUnit5Predicates.annotatedWithTestOrParameterizedTest; import static com.tngtech.archunit.lang.conditions.ArchPredicates.are; +import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses; import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noMethods; @@ -38,6 +39,18 @@ public JUnit5Configurer methodsShouldMatch(String regex, Configuration configura configuration)); } + public JUnit5Configurer classesShouldBePackagePrivate(String regex) { + return classesShouldBePackagePrivate(regex, CONFIGURATION); + } + + public JUnit5Configurer classesShouldBePackagePrivate(String regex, Configuration configuration) { + return addRule(TaikaiRule.of(classes() + .that().haveNameMatching(regex) + .should().bePackagePrivate() + .as("Classes with names matching %s should be package-private".formatted(regex)), + configuration)); + } + public JUnit5Configurer methodsShouldNotDeclareExceptions() { return methodsShouldNotDeclareExceptions(CONFIGURATION); } diff --git a/src/test/java/com/enofex/taikai/ArchitectureTest.java b/src/test/java/com/enofex/taikai/ArchitectureTest.java index f5eb2f2..ccc60fb 100644 --- a/src/test/java/com/enofex/taikai/ArchitectureTest.java +++ b/src/test/java/com/enofex/taikai/ArchitectureTest.java @@ -14,21 +14,22 @@ void shouldFulfilConstrains() { .test(test -> test .junit5(junit5 -> junit5 .classesShouldNotBeAnnotatedWithDisabled() + .classesShouldBePackagePrivate(".*Test") .methodsShouldNotBeAnnotatedWithDisabled() .methodsShouldMatch("should.*") .methodsShouldBePackagePrivate() .methodsShouldNotDeclareExceptions())) .java(java -> java .noUsageOfDeprecatedAPIs() - .finalClassesShouldNotHaveProtectedMembers() - .classesShouldImplementHashCodeAndEquals() - .methodsShouldNotDeclareGenericExceptions() - .utilityClassesShouldBeFinalAndHavePrivateConstructor() - .fieldsShouldNotBePublic() .noUsageOfSystemOutOrErr() .noUsageOf(Date.class) .noUsageOf(Calendar.class) .noUsageOf(SimpleDateFormat.class) + .classesShouldImplementHashCodeAndEquals() + .finalClassesShouldNotHaveProtectedMembers() + .utilityClassesShouldBeFinalAndHavePrivateConstructor() + .methodsShouldNotDeclareGenericExceptions() + .fieldsShouldNotBePublic() .imports(imports -> imports .shouldHaveNoCycles() .shouldNotImport("..shaded..") diff --git a/src/test/java/com/enofex/taikai/Usage.java b/src/test/java/com/enofex/taikai/Usage.java index e3c63fb..482d7e5 100644 --- a/src/test/java/com/enofex/taikai/Usage.java +++ b/src/test/java/com/enofex/taikai/Usage.java @@ -3,13 +3,10 @@ import java.util.Calendar; import java.util.Date; -final class Usage { - - private Usage() { - } +class Usage { public static void main(String[] args) { - Taikai taikai = Taikai.builder() + Taikai.builder() .namespace("com.enofex.taikai") .spring(spring -> spring .noAutowiredFields() @@ -38,8 +35,9 @@ public static void main(String[] args) { .methodsShouldMatch("should.*") .methodsShouldBePackagePrivate() .methodsShouldBeAnnotatedWithDisplayName() - .classesShouldNotBeAnnotatedWithDisabled() - .methodsShouldNotBeAnnotatedWithDisabled())) + .methodsShouldNotBeAnnotatedWithDisabled() + .classesShouldBePackagePrivate(".*Test") + .classesShouldNotBeAnnotatedWithDisabled())) .java(java -> java .noUsageOf(Date.class) .noUsageOf(Calendar.class) @@ -61,8 +59,7 @@ public static void main(String[] args) { .fieldsShouldNotMatch("bar") .constantsShouldFollowConvention() .interfacesShouldNotHavePrefixI())) - .build(); - - taikai.check(); + .build() + .check(); } }