de.cronn.testutils.JUnit5MisusageCheck Maven / Gradle / Ivy
The newest version!
package de.cronn.testutils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class JUnit5MisusageCheck implements BeforeAllCallback {
@Override
public void beforeAll(ExtensionContext context) throws Exception {
Class> clazz = context.getRequiredTestClass();
List> topDownHierarchy = getTopDownHierarchy(clazz);
List nonCompliantMethods = new ArrayList<>();
for (Class extends Annotation> annotation : Arrays.asList(BeforeEach.class, Test.class, AfterEach.class)) {
nonCompliantMethods.addAll(checkInstanceMethodMisusage(topDownHierarchy, annotation));
}
for (Class extends Annotation> annotation : Arrays.asList(BeforeAll.class, AfterAll.class)) {
nonCompliantMethods.addAll(checkStaticMethodMisusage(topDownHierarchy, annotation));
}
if (!nonCompliantMethods.isEmpty()) {
throw new IllegalStateException(
nonCompliantMethods.stream()
.map(Method::toString)
.collect(
Collectors.joining(
"\n",
"Misused junit5 callback methods: \n",
""
)
)
);
}
}
private List checkInstanceMethodMisusage(List> topDownHierarchy, Class extends Annotation> annotation) {
List misusedMethods = new ArrayList<>();
List annotatedMethodsFromAncestors = new ArrayList<>();
for (Class> clazz : topDownHierarchy) {
List clazzInstanceMethods = getDeclaredInstanceMethods(clazz);
for (Method overriddenMethod : findOverriddenMethods(annotatedMethodsFromAncestors, clazzInstanceMethods)) {
if (overriddenMethod.getAnnotation(annotation) == null) {
misusedMethods.add(overriddenMethod);
}
}
annotatedMethodsFromAncestors.addAll(getAnnotatedMethods(annotation, clazzInstanceMethods));
}
return misusedMethods;
}
private List getDeclaredInstanceMethods(Class> clazz) {
return Stream.of(clazz.getDeclaredMethods())
.filter(m -> !Modifier.isStatic(m.getModifiers()))
.collect(Collectors.toList());
}
private List findOverriddenMethods(List ancestorMethods, List methodsToCheck) {
List overriddenMethods = new ArrayList<>();
for (Method methodToCheck : methodsToCheck) {
for (Method ancestorMethod : ancestorMethods) {
if (isInstanceMethodOverridden(methodToCheck, ancestorMethod)) {
overriddenMethods.add(methodToCheck);
}
}
}
return overriddenMethods;
}
private List checkStaticMethodMisusage(List> topDownHierarchy, Class extends Annotation> annotation) {
List misusedMethods = new ArrayList<>();
List annotatedMethodsFromAncestors = new ArrayList<>();
for (Class> clazz : topDownHierarchy) {
List clazzDeclaredStaticMethods = getDeclaredStaticMethods(clazz);
misusedMethods.addAll(findHiddenMethods(annotatedMethodsFromAncestors, clazzDeclaredStaticMethods));
annotatedMethodsFromAncestors.addAll(getAnnotatedMethods(annotation, clazzDeclaredStaticMethods));
}
return misusedMethods;
}
private List getDeclaredStaticMethods(Class> clazz) {
return Stream.of(clazz.getDeclaredMethods())
.filter(m -> Modifier.isStatic(m.getModifiers()))
.collect(Collectors.toList());
}
private List findHiddenMethods(List ancestorMethods, List methodsToCheck) {
List hiddenMethods = new ArrayList<>();
for (Method methodToCheck : methodsToCheck) {
for (Method ancestorMethod : ancestorMethods) {
if (isStaticMethodHidden(methodToCheck, ancestorMethod)) {
hiddenMethods.add(methodToCheck);
}
}
}
return hiddenMethods;
}
private List getAnnotatedMethods(Class extends Annotation> annotation, List methods) {
return methods.stream()
.filter(method -> method.getAnnotation(annotation) != null)
.collect(Collectors.toList());
}
private boolean isInstanceMethodOverridden(Method childMethod, Method parentMethod) {
boolean namesMatch = childMethod.getName().equals(parentMethod.getName());
boolean parameterTypesMatch = Arrays.equals(childMethod.getParameterTypes(), parentMethod.getParameterTypes());
boolean returnTypesMatch = parentMethod.getReturnType().isAssignableFrom(childMethod.getReturnType());
return namesMatch && parameterTypesMatch && returnTypesMatch;
}
private boolean isStaticMethodHidden(Method childMethod, Method parentMethod) {
boolean namesMatch = childMethod.getName().equals(parentMethod.getName());
boolean parameterTypesMatch = Arrays.equals(childMethod.getParameterTypes(), parentMethod.getParameterTypes());
return namesMatch && parameterTypesMatch;
}
private List> getTopDownHierarchy(Class> clazz) {
List> bottomUpHierarchy = new ArrayList<>();
bottomUpHierarchy.add(clazz);
while (clazz.getSuperclass() != Object.class) {
clazz = clazz.getSuperclass();
bottomUpHierarchy.add(clazz);
}
Collections.reverse(bottomUpHierarchy);
return bottomUpHierarchy;
}
}