package org.junit.internal.runners.rules;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.rules.MethodRule;
import org.junit.rules.TestRule;
import org.junit.runners.model.FrameworkMember;
import org.junit.runners.model.TestClass;

import java.lang.annotation.Annotation;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

A RuleMemberValidator validates the rule fields/methods of a TestClass. All reasons for rejecting the TestClass are written to a list of errors.

There are four slightly different validators. The CLASS_RULE_VALIDATOR validates fields with a ClassRule annotation and the RULE_VALIDATOR validates fields with a Rule annotation.

The CLASS_RULE_METHOD_VALIDATOR validates methods with a ClassRule annotation and the RULE_METHOD_VALIDATOR validates methods with a Rule annotation.

/** * A RuleMemberValidator validates the rule fields/methods of a * {@link org.junit.runners.model.TestClass}. All reasons for rejecting the * {@code TestClass} are written to a list of errors. * * <p>There are four slightly different validators. The {@link #CLASS_RULE_VALIDATOR} * validates fields with a {@link ClassRule} annotation and the * {@link #RULE_VALIDATOR} validates fields with a {@link Rule} annotation.</p> * * <p>The {@link #CLASS_RULE_METHOD_VALIDATOR} * validates methods with a {@link ClassRule} annotation and the * {@link #RULE_METHOD_VALIDATOR} validates methods with a {@link Rule} annotation.</p> */
public class RuleMemberValidator {
Validates fields with a ClassRule annotation.
/** * Validates fields with a {@link ClassRule} annotation. */
public static final RuleMemberValidator CLASS_RULE_VALIDATOR = classRuleValidatorBuilder() .withValidator(new DeclaringClassMustBePublic()) .withValidator(new MemberMustBeStatic()) .withValidator(new MemberMustBePublic()) .withValidator(new FieldMustBeATestRule()) .build();
Validates fields with a Rule annotation.
/** * Validates fields with a {@link Rule} annotation. */
public static final RuleMemberValidator RULE_VALIDATOR = testRuleValidatorBuilder() .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) .withValidator(new MemberMustBePublic()) .withValidator(new FieldMustBeARule()) .build();
Validates methods with a ClassRule annotation.
/** * Validates methods with a {@link ClassRule} annotation. */
public static final RuleMemberValidator CLASS_RULE_METHOD_VALIDATOR = classRuleValidatorBuilder() .forMethods() .withValidator(new DeclaringClassMustBePublic()) .withValidator(new MemberMustBeStatic()) .withValidator(new MemberMustBePublic()) .withValidator(new MethodMustBeATestRule()) .build();
Validates methods with a Rule annotation.
/** * Validates methods with a {@link Rule} annotation. */
public static final RuleMemberValidator RULE_METHOD_VALIDATOR = testRuleValidatorBuilder() .forMethods() .withValidator(new MemberMustBeNonStaticOrAlsoClassRule()) .withValidator(new MemberMustBePublic()) .withValidator(new MethodMustBeARule()) .build(); private final Class<? extends Annotation> annotation; private final boolean methods; private final List<RuleValidator> validatorStrategies; RuleMemberValidator(Builder builder) { this.annotation = builder.annotation; this.methods = builder.methods; this.validatorStrategies = builder.validators; }
Validate the TestClass and adds reasons for rejecting the class to a list of errors.
Params:
  • target – the TestClass to validate.
  • errors – the list of errors.
/** * Validate the {@link org.junit.runners.model.TestClass} and adds reasons * for rejecting the class to a list of errors. * * @param target the {@code TestClass} to validate. * @param errors the list of errors. */
public void validate(TestClass target, List<Throwable> errors) { List<? extends FrameworkMember<?>> members = methods ? target.getAnnotatedMethods(annotation) : target.getAnnotatedFields(annotation); for (FrameworkMember<?> each : members) { validateMember(each, errors); } } private void validateMember(FrameworkMember<?> member, List<Throwable> errors) { for (RuleValidator strategy : validatorStrategies) { strategy.validate(member, annotation, errors); } } private static Builder classRuleValidatorBuilder() { return new Builder(ClassRule.class); } private static Builder testRuleValidatorBuilder() { return new Builder(Rule.class); } private static class Builder { private final Class<? extends Annotation> annotation; private boolean methods; private final List<RuleValidator> validators; private Builder(Class<? extends Annotation> annotation) { this.annotation = annotation; this.methods = false; this.validators = new ArrayList<RuleValidator>(); } Builder forMethods() { methods = true; return this; } Builder withValidator(RuleValidator validator) { validators.add(validator); return this; } RuleMemberValidator build() { return new RuleMemberValidator(this); } } private static boolean isRuleType(FrameworkMember<?> member) { return isMethodRule(member) || isTestRule(member); } private static boolean isTestRule(FrameworkMember<?> member) { return TestRule.class.isAssignableFrom(member.getType()); } private static boolean isMethodRule(FrameworkMember<?> member) { return MethodRule.class.isAssignableFrom(member.getType()); }
Encapsulates a single piece of validation logic, used to determine if Rule and ClassRule annotations have been used correctly
/** * Encapsulates a single piece of validation logic, used to determine if {@link org.junit.Rule} and * {@link org.junit.ClassRule} annotations have been used correctly */
interface RuleValidator {
Examine the given member and add any violations of the strategy's validation logic to the given list of errors
Params:
  • member – The member (field or member) to examine
  • annotation – The type of rule annotation on the member
  • errors – The list of errors to add validation violations to
/** * Examine the given member and add any violations of the strategy's validation logic to the given list of errors * @param member The member (field or member) to examine * @param annotation The type of rule annotation on the member * @param errors The list of errors to add validation violations to */
void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors); }
Requires the validated member to be non-static
/** * Requires the validated member to be non-static */
private static final class MemberMustBeNonStaticOrAlsoClassRule implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { boolean isMethodRuleMember = isMethodRule(member); boolean isClassRuleAnnotated = (member.getAnnotation(ClassRule.class) != null); // We disallow: // - static MethodRule members // - static @Rule annotated members // - UNLESS they're also @ClassRule annotated // Note that MethodRule cannot be annotated with @ClassRule if (member.isStatic() && (isMethodRuleMember || !isClassRuleAnnotated)) { String message; if (isMethodRule(member)) { message = "must not be static."; } else { message = "must not be static or it must be annotated with @ClassRule."; } errors.add(new ValidationError(member, annotation, message)); } } }
Requires the member to be static
/** * Requires the member to be static */
private static final class MemberMustBeStatic implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!member.isStatic()) { errors.add(new ValidationError(member, annotation, "must be static.")); } } }
Requires the member's declaring class to be public
/** * Requires the member's declaring class to be public */
private static final class DeclaringClassMustBePublic implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!isDeclaringClassPublic(member)) { errors.add(new ValidationError(member, annotation, "must be declared in a public class.")); } } private boolean isDeclaringClassPublic(FrameworkMember<?> member) { return Modifier.isPublic(member.getDeclaringClass().getModifiers()); } }
Requires the member to be public
/** * Requires the member to be public */
private static final class MemberMustBePublic implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!member.isPublic()) { errors.add(new ValidationError(member, annotation, "must be public.")); } } }
Requires the member is a field implementing MethodRule or TestRule
/** * Requires the member is a field implementing {@link org.junit.rules.MethodRule} or {@link org.junit.rules.TestRule} */
private static final class FieldMustBeARule implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!isRuleType(member)) { errors.add(new ValidationError(member, annotation, "must implement MethodRule or TestRule.")); } } }
Require the member to return an implementation of MethodRule or TestRule
/** * Require the member to return an implementation of {@link org.junit.rules.MethodRule} or * {@link org.junit.rules.TestRule} */
private static final class MethodMustBeARule implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!isRuleType(member)) { errors.add(new ValidationError(member, annotation, "must return an implementation of MethodRule or TestRule.")); } } }
Require the member to return an implementation of TestRule
/** * Require the member to return an implementation of {@link org.junit.rules.TestRule} */
private static final class MethodMustBeATestRule implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!isTestRule(member)) { errors.add(new ValidationError(member, annotation, "must return an implementation of TestRule.")); } } }
Requires the member is a field implementing TestRule
/** * Requires the member is a field implementing {@link org.junit.rules.TestRule} */
private static final class FieldMustBeATestRule implements RuleValidator { public void validate(FrameworkMember<?> member, Class<? extends Annotation> annotation, List<Throwable> errors) { if (!isTestRule(member)) { errors.add(new ValidationError(member, annotation, "must implement TestRule.")); } } } }