org.junit.runners.model.TestClass Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of junit Show documentation
Show all versions of junit Show documentation
JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
The newest version!
package org.junit.runners.model;
import static java.lang.reflect.Modifier.isStatic;
import static org.junit.internal.MethodSorter.NAME_ASCENDING;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
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.Comparator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.internal.MethodSorter;
/**
* Wraps a class to be run, providing method validation and annotation searching
*
* @since 4.5
*/
public class TestClass implements Annotatable {
private static final FieldComparator FIELD_COMPARATOR = new FieldComparator();
private static final MethodComparator METHOD_COMPARATOR = new MethodComparator();
private final Class> clazz;
private final Map, List> methodsForAnnotations;
private final Map, List> fieldsForAnnotations;
/**
* Creates a {@code TestClass} wrapping {@code clazz}. Each time this
* constructor executes, the class is scanned for annotations, which can be
* an expensive process (we hope in future JDK's it will not be.) Therefore,
* try to share instances of {@code TestClass} where possible.
*/
public TestClass(Class> clazz) {
this.clazz = clazz;
if (clazz != null && clazz.getConstructors().length > 1) {
throw new IllegalArgumentException(
"Test class can only have one constructor");
}
Map, List> methodsForAnnotations =
new LinkedHashMap, List>();
Map, List> fieldsForAnnotations =
new LinkedHashMap, List>();
scanAnnotatedMembers(methodsForAnnotations, fieldsForAnnotations);
this.methodsForAnnotations = makeDeeplyUnmodifiable(methodsForAnnotations);
this.fieldsForAnnotations = makeDeeplyUnmodifiable(fieldsForAnnotations);
}
protected void scanAnnotatedMembers(Map, List> methodsForAnnotations, Map, List> fieldsForAnnotations) {
for (Class> eachClass : getSuperClasses(clazz)) {
for (Method eachMethod : MethodSorter.getDeclaredMethods(eachClass)) {
addToAnnotationLists(new FrameworkMethod(eachMethod), methodsForAnnotations);
}
// ensuring fields are sorted to make sure that entries are inserted
// and read from fieldForAnnotations in a deterministic order
for (Field eachField : getSortedDeclaredFields(eachClass)) {
addToAnnotationLists(new FrameworkField(eachField), fieldsForAnnotations);
}
}
}
private static Field[] getSortedDeclaredFields(Class> clazz) {
Field[] declaredFields = clazz.getDeclaredFields();
Arrays.sort(declaredFields, FIELD_COMPARATOR);
return declaredFields;
}
protected static > void addToAnnotationLists(T member,
Map, List> map) {
for (Annotation each : member.getAnnotations()) {
Class extends Annotation> type = each.annotationType();
List members = getAnnotatedMembers(map, type, true);
if (member.isShadowedBy(members)) {
return;
}
if (runsTopToBottom(type)) {
members.add(0, member);
} else {
members.add(member);
}
}
}
private static > Map, List>
makeDeeplyUnmodifiable(Map, List> source) {
LinkedHashMap, List> copy =
new LinkedHashMap, List>();
for (Map.Entry, List> entry : source.entrySet()) {
copy.put(entry.getKey(), Collections.unmodifiableList(entry.getValue()));
}
return Collections.unmodifiableMap(copy);
}
/**
* Returns, efficiently, all the non-overridden methods in this class and
* its superclasses that are annotated}.
*
* @since 4.12
*/
public List getAnnotatedMethods() {
List methods = collectValues(methodsForAnnotations);
Collections.sort(methods, METHOD_COMPARATOR);
return methods;
}
/**
* Returns, efficiently, all the non-overridden methods in this class and
* its superclasses that are annotated with {@code annotationClass}.
*/
public List getAnnotatedMethods(
Class extends Annotation> annotationClass) {
return Collections.unmodifiableList(getAnnotatedMembers(methodsForAnnotations, annotationClass, false));
}
/**
* Returns, efficiently, all the non-overridden fields in this class and its
* superclasses that are annotated.
*
* @since 4.12
*/
public List getAnnotatedFields() {
return collectValues(fieldsForAnnotations);
}
/**
* Returns, efficiently, all the non-overridden fields in this class and its
* superclasses that are annotated with {@code annotationClass}.
*/
public List getAnnotatedFields(
Class extends Annotation> annotationClass) {
return Collections.unmodifiableList(getAnnotatedMembers(fieldsForAnnotations, annotationClass, false));
}
private List collectValues(Map, List> map) {
Set values = new LinkedHashSet();
for (List additionalValues : map.values()) {
values.addAll(additionalValues);
}
return new ArrayList(values);
}
private static List getAnnotatedMembers(Map, List> map,
Class extends Annotation> type, boolean fillIfAbsent) {
if (!map.containsKey(type) && fillIfAbsent) {
map.put(type, new ArrayList());
}
List members = map.get(type);
return members == null ? Collections.emptyList() : members;
}
private static boolean runsTopToBottom(Class extends Annotation> annotation) {
return annotation.equals(Before.class)
|| annotation.equals(BeforeClass.class);
}
private static List> getSuperClasses(Class> testClass) {
ArrayList> results = new ArrayList>();
Class> current = testClass;
while (current != null) {
results.add(current);
current = current.getSuperclass();
}
return results;
}
/**
* Returns the underlying Java class.
*/
public Class> getJavaClass() {
return clazz;
}
/**
* Returns the class's name.
*/
public String getName() {
if (clazz == null) {
return "null";
}
return clazz.getName();
}
/**
* Returns the only public constructor in the class, or throws an {@code
* AssertionError} if there are more or less than one.
*/
public Constructor> getOnlyConstructor() {
Constructor>[] constructors = clazz.getConstructors();
Assert.assertEquals(1, constructors.length);
return constructors[0];
}
/**
* Returns the annotations on this class
*/
public Annotation[] getAnnotations() {
if (clazz == null) {
return new Annotation[0];
}
return clazz.getAnnotations();
}
public T getAnnotation(Class annotationType) {
if (clazz == null) {
return null;
}
return clazz.getAnnotation(annotationType);
}
public List getAnnotatedFieldValues(Object test,
Class extends Annotation> annotationClass, Class valueClass) {
List results = new ArrayList();
for (FrameworkField each : getAnnotatedFields(annotationClass)) {
try {
Object fieldValue = each.get(test);
if (valueClass.isInstance(fieldValue)) {
results.add(valueClass.cast(fieldValue));
}
} catch (IllegalAccessException e) {
throw new RuntimeException(
"How did getFields return a field we couldn't access?", e);
}
}
return results;
}
public List getAnnotatedMethodValues(Object test,
Class extends Annotation> annotationClass, Class valueClass) {
List results = new ArrayList();
for (FrameworkMethod each : getAnnotatedMethods(annotationClass)) {
try {
/*
* A method annotated with @Rule may return a @TestRule or a @MethodRule,
* we cannot call the method to check whether the return type matches our
* expectation i.e. subclass of valueClass. If we do that then the method
* will be invoked twice and we do not want to do that. So we first check
* whether return type matches our expectation and only then call the method
* to fetch the MethodRule
*/
if (valueClass.isAssignableFrom(each.getReturnType())) {
Object fieldValue = each.invokeExplosively(test);
results.add(valueClass.cast(fieldValue));
}
} catch (Throwable e) {
throw new RuntimeException(
"Exception in " + each.getName(), e);
}
}
return results;
}
public boolean isPublic() {
return Modifier.isPublic(clazz.getModifiers());
}
public boolean isANonStaticInnerClass() {
return clazz.isMemberClass() && !isStatic(clazz.getModifiers());
}
@Override
public int hashCode() {
return (clazz == null) ? 0 : clazz.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TestClass other = (TestClass) obj;
return clazz == other.clazz;
}
/**
* Compares two fields by its name.
*/
private static class FieldComparator implements Comparator {
public int compare(Field left, Field right) {
return left.getName().compareTo(right.getName());
}
}
/**
* Compares two methods by its name.
*/
private static class MethodComparator implements
Comparator {
public int compare(FrameworkMethod left, FrameworkMethod right) {
return NAME_ASCENDING.compare(left.getMethod(), right.getMethod());
}
}
}