Skip to content

Commit

Permalink
Provide a new JUnit 5 Rule checking if the test methods do not declar…
Browse files Browse the repository at this point in the history
…e any thrown exceptions

Closes gh-30
  • Loading branch information
mnhock committed Jun 16, 2024
1 parent 6da4385 commit f8e43d4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 26 deletions.
53 changes: 33 additions & 20 deletions docs/USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,26 +39,27 @@ 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 (WITH_TESTS) |
| | JUnit 5 | `methodsShouldNotBeAnnotatedWithDisabled` | Ensure methods are not annotated with `@Disabled` | Default (WITH_TESTS) |
| | JUnit 5 | `methodsShouldBePackagePrivate` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` are package-private. | Default (WITH_TESTS) |
| | JUnit 5 | `methodsShouldBeAnnotatedWithDisplayName` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` are annotated with `@DisplayName`. | Default (WITH_TESTS) |
| | JUnit 5 | `methodsShouldMatch` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` have names matching a specific regex pattern. | Default (WITH_TESTS) |
| **Spring** | General | `noAutowiredFields` | Fields should not be annotated with `@Autowired` (prefer constructor injection) | Default (WITH_TESTS) |
| | Boot | `springBootApplicationShouldBeIn` | Ensure `@SpringBootApplication` is in the default package | Default (WITH_TESTS) |
| | Configurations | `namesShouldEndWithConfiguration` | Configuration classes should end with "Configuration" | Default (WITH_TESTS) |
| | Configurations | `namesShouldMatch` | Configuration classes should match a regex pattern | Default (WITH_TESTS) |
| | Controllers | `namesShouldEndWithController` | Controllers should end with "Controller" | Default (WITH_TESTS) |
| | Controllers | `namesShouldMatch` | Controllers should match a regex pattern | Default (WITH_TESTS) |
| | Controllers | `shouldBeAnnotatedWithRestController` | Controllers should be annotated with `@RestController` | Default (WITH_TESTS) |
| | Controllers | `shouldBePackagePrivate` | Controllers should be package-private | Default (WITH_TESTS) |
| | Controllers | `shouldNotDependOnOtherControllers` | Controllers should not depend on other controllers | Default (WITH_TESTS) |
| | Repositories | `namesShouldEndWithRepository` | Repositories should end with "Repository" | Default (WITH_TESTS) |
| | Repositories | `namesShouldMatch` | Repositories should match a regex pattern | Default (WITH_TESTS) |
| | Repositories | `shouldBeAnnotatedWithRepository` | Repositories should be annotated with `@Repository` | Default (WITH_TESTS) |
| | Services | `namesShouldEndWithService` | Services should end with "Service" | Default (WITH_TESTS) |
| | Services | `namesShouldMatch` | Services should match a regex pattern | Default (WITH_TESTS) |
| | Services | `shouldBeAnnotatedWithService` | Services should be annotated with `@Service` | Default (WITH_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) |
| | JUnit 5 | `methodsShouldBePackagePrivate` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` are package-private. | 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 | `methodsShouldNotDeclareThrownExceptions` | Ensure that test methods annotated with `@Test` or `@ParameterizedTest` do not declare any thrown exceptions. | Default (ONLY_TESTS) |
| **Spring** | General | `noAutowiredFields` | Fields should not be annotated with `@Autowired` (prefer constructor injection) | Default (WITHOUT_TESTS) |
| | Boot | `springBootApplicationShouldBeIn` | Ensure `@SpringBootApplication` is in the default package | Default (WITHOUT_TESTS) |
| | Configurations | `namesShouldEndWithConfiguration` | Configuration classes should end with "Configuration" | Default (WITHOUT_TESTS) |
| | Configurations | `namesShouldMatch` | Configuration classes should match a regex pattern | Default (WITHOUT_TESTS) |
| | Controllers | `namesShouldEndWithController` | Controllers should end with "Controller" | Default (WITHOUT_TESTS) |
| | Controllers | `namesShouldMatch` | Controllers should match a regex pattern | Default (WITHOUT_TESTS) |
| | Controllers | `shouldBeAnnotatedWithRestController` | Controllers should be annotated with `@RestController` | Default (WITHOUT_TESTS) |
| | Controllers | `shouldBePackagePrivate` | Controllers should be package-private | Default (WITHOUT_TESTS) |
| | Controllers | `shouldNotDependOnOtherControllers` | Controllers should not depend on other controllers | Default (WITHOUT_TESTS) |
| | Repositories | `namesShouldEndWithRepository` | Repositories should end with "Repository" | Default (WITHOUT_TESTS) |
| | Repositories | `namesShouldMatch` | Repositories should match a regex pattern | Default (WITHOUT_TESTS) |
| | Repositories | `shouldBeAnnotatedWithRepository` | Repositories should be annotated with `@Repository` | Default (WITHOUT_TESTS) |
| | Services | `namesShouldEndWithService` | Services should end with "Service" | Default (WITHOUT_TESTS) |
| | Services | `namesShouldMatch` | Services should match a regex pattern | Default (WITHOUT_TESTS) |
| | Services | `shouldBeAnnotatedWithService` | Services should be annotated with `@Service` | Default (WITHOUT_TESTS) |

### Java Configuration

Expand Down Expand Up @@ -238,6 +239,18 @@ 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.

```java
Taikai.builder()
.namespace("com.company.yourproject")
.test(test -> test
.junit5(junit5 -> junit5
.methodsShouldNotDeclareThrownExceptions()))
.build()
.check();
```

### Spring Configuration

Spring configuration involves defining constraints specific to Spring Framework usage.
Expand Down
44 changes: 38 additions & 6 deletions src/main/java/com/enofex/taikai/test/JUnit5Configurer.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,26 @@
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClasses;
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noMethods;

import com.enofex.taikai.Namespace;
import com.enofex.taikai.Namespace.IMPORT;
import com.enofex.taikai.TaikaiRule;
import com.enofex.taikai.TaikaiRule.Configuration;
import com.enofex.taikai.configures.AbstractConfigurer;
import com.enofex.taikai.configures.ConfigurerContext;
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 final class JUnit5Configurer extends AbstractConfigurer {

private static final Configuration CONFIGURATION = Configuration.of(IMPORT.ONLY_TESTS);

JUnit5Configurer(ConfigurerContext configurerContext) {
super(configurerContext);
}

public JUnit5Configurer methodsShouldMatch(String regex) {
return methodsShouldMatch(regex, null);
return methodsShouldMatch(regex, CONFIGURATION);
}

public JUnit5Configurer methodsShouldMatch(String regex, Configuration configuration) {
Expand All @@ -35,8 +41,21 @@ public JUnit5Configurer methodsShouldMatch(String regex, Configuration configura
configuration));
}

public JUnit5Configurer methodsShouldNotDeclareThrownExceptions() {
return methodsShouldNotDeclareThrownExceptions(CONFIGURATION);
}

public JUnit5Configurer methodsShouldNotDeclareThrownExceptions(Configuration configuration) {
return addRule(TaikaiRule.of(methods()
.that(are(annotatedWithTestOrParameterizedTest(true)))
.should(notDeclareThrownExceptions())
.as("Methods annotated with %s or %s should not declare thrown Exceptions".formatted(
ANNOTATION_TEST, ANNOTATION_PARAMETRIZED_TEST)),
configuration));
}

public JUnit5Configurer methodsShouldBeAnnotatedWithDisplayName() {
return methodsShouldBeAnnotatedWithDisplayName(Configuration.of(Namespace.IMPORT.WITH_TESTS));
return methodsShouldBeAnnotatedWithDisplayName(CONFIGURATION);
}

public JUnit5Configurer methodsShouldBeAnnotatedWithDisplayName(Configuration configuration) {
Expand All @@ -49,7 +68,7 @@ public JUnit5Configurer methodsShouldBeAnnotatedWithDisplayName(Configuration co
}

public JUnit5Configurer methodsShouldBePackagePrivate() {
return methodsShouldBePackagePrivate(Configuration.of(Namespace.IMPORT.WITH_TESTS));
return methodsShouldBePackagePrivate(CONFIGURATION);
}

public JUnit5Configurer methodsShouldBePackagePrivate(Configuration configuration) {
Expand All @@ -62,7 +81,7 @@ public JUnit5Configurer methodsShouldBePackagePrivate(Configuration configuratio
}

public JUnit5Configurer methodsShouldNotBeAnnotatedWithDisabled() {
return methodsShouldNotBeAnnotatedWithDisabled(Configuration.of(Namespace.IMPORT.WITH_TESTS));
return methodsShouldNotBeAnnotatedWithDisabled(CONFIGURATION);
}

public JUnit5Configurer methodsShouldNotBeAnnotatedWithDisabled(Configuration configuration) {
Expand All @@ -73,7 +92,7 @@ public JUnit5Configurer methodsShouldNotBeAnnotatedWithDisabled(Configuration co
}

public JUnit5Configurer classesShouldNotBeAnnotatedWithDisabled() {
return classesShouldNotBeAnnotatedWithDisabled(Configuration.of(Namespace.IMPORT.WITH_TESTS));
return classesShouldNotBeAnnotatedWithDisabled(CONFIGURATION);
}

public JUnit5Configurer classesShouldNotBeAnnotatedWithDisabled(Configuration configuration) {
Expand All @@ -82,4 +101,17 @@ public JUnit5Configurer classesShouldNotBeAnnotatedWithDisabled(Configuration co
.as("Classes should not be annotated with %s".formatted(ANNOTATION_DISABLED)),
configuration));
}

private ArchCondition<JavaMethod> notDeclareThrownExceptions() {
return new ArchCondition<>("not declare thrown exceptions") {
@Override
public void check(JavaMethod method, ConditionEvents events) {
if (!method.getThrowsClause().isEmpty()) {
String message = String.format("Method %s declares thrown exceptions",
method.getFullName());
events.add(SimpleConditionEvent.violated(method, message));
}
}
};
}
}
1 change: 1 addition & 0 deletions src/test/java/com/enofex/taikai/ArchitectureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ void shouldFulfilConstrains() {
.namespace("com.enofex.taikai")
.test(test -> test
.junit5(junit5 -> junit5
.methodsShouldNotDeclareThrownExceptions()
.methodsShouldMatch("should.*")
.methodsShouldBePackagePrivate()
.classesShouldNotBeAnnotatedWithDisabled()
Expand Down
1 change: 1 addition & 0 deletions src/test/java/com/enofex/taikai/Usage.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public static void main(String[] args) {
.namesShouldEndWithRepository()))
.test(test -> test
.junit5(junit5 -> junit5
.methodsShouldNotDeclareThrownExceptions()
.methodsShouldMatch("should.*")
.methodsShouldBePackagePrivate()
.methodsShouldBeAnnotatedWithDisplayName()
Expand Down

0 comments on commit f8e43d4

Please sign in to comment.