Source code
package org.junit.runners;
import static org.junit.internal.runners.rules.RuleFieldValidator.RULE_METHOD_VALIDATOR;
import static org.junit.internal.runners.rules.RuleFieldValidator.RULE_VALIDATOR;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.Test.None;
import org.junit.internal.runners.model.ReflectiveCallable;
import org.junit.internal.runners.statements.ExpectException;
import org.junit.internal.runners.statements.Fail;
import org.junit.internal.runners.statements.FailOnTimeout;
import org.junit.internal.runners.statements.InvokeMethod;
import org.junit.internal.runners.statements.RunAfters;
import org.junit.internal.runners.statements.RunBefores;
import org.junit.rules.RunRules;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
/**
* Implements the JUnit 4 standard test case class model, as defined by the
* annotations in the org.junit package. Many users will never notice this
* class: it is now the default test class runner, but it should have exactly
* the same behavior as the old test class runner ({@code JUnit4ClassRunner}).
* <p>
* BlockJUnit4ClassRunner has advantages for writers of custom JUnit runners
* that are slight changes to the default behavior, however:
*
* <ul>
* <li>It has a much simpler implementation based on {@link Statement}s,
* allowing new operations to be inserted into the appropriate point in the
* execution flow.
*
* <li>It is published, and extension and reuse are encouraged, whereas {@code
* JUnit4ClassRunner} was in an internal package, and is now deprecated.
* </ul>
* <p>
* In turn, in 2009 we introduced {@link Rule}s. In many cases where extending
* BlockJUnit4ClassRunner was necessary to add new behavior, {@link Rule}s can
* be used, which makes the extension more reusable and composable.
*
* @since 4.5
*/
public class BlockJUnit4ClassRunner extends ParentRunner<FrameworkMethod> {
private final ConcurrentHashMap<FrameworkMethod, Description> fMethodDescriptions = new ConcurrentHashMap<FrameworkMethod, Description>();
/**
* Creates a BlockJUnit4ClassRunner to run {@code klass}
*
* @throws InitializationError if the test class is malformed.
*/
public BlockJUnit4ClassRunner(Class<?> klass) throws InitializationError {
super(klass);
}
//
// Implementation of ParentRunner
//
@Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
runLeaf(methodBlock(method), description, notifier);
}
}
/**
* Evaluates whether {@link FrameworkMethod}s are ignored based on the
* {@link Ignore} annotation.
*/
@Override
protected boolean isIgnored(FrameworkMethod child) {
return child.getAnnotation(Ignore.class) != null;
}
@Override
protected Description describeChild(FrameworkMethod method) {
Description description = fMethodDescriptions.get(method);
if (description == null) {
description = Description.createTestDescription(getTestClass().getJavaClass(),
testName(method), method.getAnnotations());
fMethodDescriptions.putIfAbsent(method, description);
}
return description;
}
@Override
protected List<FrameworkMethod> getChildren() {
return computeTestMethods();
}
//
// Override in subclasses
//
/**
* Returns the methods that run tests. Default implementation returns all
* methods annotated with {@code @Test} on this class and superclasses that
* are not overridden.
*/
protected List<FrameworkMethod> computeTestMethods() {
return getTestClass().getAnnotatedMethods(Test.class);
}
@Override
protected void collectInitializationErrors(List<Throwable> errors) {
super.collectInitializationErrors(errors);
validateNoNonStaticInnerClass(errors);
validateConstructor(errors);
validateInstanceMethods(errors);
validateFields(errors);
validateMethods(errors);
}
protected void validateNoNonStaticInnerClass(List<Throwable> errors) {
if (getTestClass().isANonStaticInnerClass()) {
String gripe = "The inner class " + getTestClass().getName()
+ " is not static.";
errors.add(new Exception(gripe));
}
}
/**
* Adds to {@code errors} if the test class has more than one constructor,
* or if the constructor takes parameters. Override if a subclass requires
* different validation rules.
*/
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
validateZeroArgConstructor(errors);
}
/**
* Adds to {@code errors} if the test class has more than one constructor
* (do not override)
*/
protected void validateOnlyOneConstructor(List<Throwable> errors) {
if (!hasOneConstructor()) {
String gripe = "Test class should have exactly one public constructor";
errors.add(new Exception(gripe));
}
}
/**
* Adds to {@code errors} if the test class's single constructor takes
* parameters (do not override)
*/
protected void validateZeroArgConstructor(List<Throwable> errors) {
if (!getTestClass().isANonStaticInnerClass()
&& hasOneConstructor()
&& (getTestClass().getOnlyConstructor().getParameterTypes().length != 0)) {
String gripe = "Test class should have exactly one public zero-argument constructor";
errors.add(new Exception(gripe));
}
}
private boolean hasOneConstructor() {
return getTestClass().getJavaClass().getConstructors().length == 1;
}
/**
* Adds to {@code errors} for each method annotated with {@code @Test},
* {@code @Before}, or {@code @After} that is not a public, void instance
* method with no arguments.
*/
@Deprecated
protected void validateInstanceMethods(List<Throwable> errors) {
validatePublicVoidNoArgMethods(After.class, false, errors);
validatePublicVoidNoArgMethods(Before.class, false, errors);
validateTestMethods(errors);
if (computeTestMethods().size() == 0) {
errors.add(new Exception("No runnable methods"));
}
}
protected void validateFields(List<Throwable> errors) {
RULE_VALIDATOR.validate(getTestClass(), errors);
}
private void validateMethods(List<Throwable> errors) {
RULE_METHOD_VALIDATOR.validate(getTestClass(), errors);
}
/**
* Adds to {@code errors} for each method annotated with {@code @Test}that
* is not a public, void instance method with no arguments.
*/
protected void validateTestMethods(List<Throwable> errors) {
validatePublicVoidNoArgMethods(Test.class, false, errors);
}
/**
* Returns a new fixture for running a test. Default implementation executes
* the test class's no-argument constructor (validation should have ensured
* one exists).
*/
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance();
}
/**
* Returns the name that describes {@code method} for {@link Description}s.
* Default implementation is the method's name
*/
protected String testName(FrameworkMethod method) {
return method.getName();
}
/**
* Returns a Statement that, when executed, either returns normally if
* {@code method} passes, or throws an exception if {@code method} fails.
*
* Here is an outline of the default implementation:
*
* <ul>
* <li>Invoke {@code method} on the result of {@code createTest()}, and
* throw any exceptions thrown by either operation.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* expecting} attribute, return normally only if the previous step threw an
* exception of the correct type, and throw an exception otherwise.
* <li>HOWEVER, if {@code method}'s {@code @Test} annotation has the {@code
* timeout} attribute, throw an exception if the previous step takes more
* than the specified number of milliseconds.
* <li>ALWAYS run all non-overridden {@code @Before} methods on this class
* and superclasses before any of the previous steps; if any throws an
* Exception, stop execution and pass the exception on.
* <li>ALWAYS run all non-overridden {@code @After} methods on this class
* and superclasses after any of the previous steps; all After methods are
* always executed: exceptions thrown by previous steps are combined, if
* necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
* <li>ALWAYS allow {@code @Rule} fields to modify the execution of the
* above steps. A {@code Rule} may prevent all execution of the above steps,
* or add additional behavior before and after, or modify thrown exceptions.
* For more information, see {@link TestRule}
* </ul>
*
* This can be overridden in subclasses, either by overriding this method,
* or the implementations creating each sub-statement.
*/
protected Statement methodBlock(FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest();
}
}.run();
} catch (Throwable e) {
return new Fail(e);
}
Statement statement = methodInvoker(method, test);
statement = possiblyExpectingExceptions(method, test, statement);
statement = withPotentialTimeout(method, test, statement);
statement = withBefores(method, test, statement);
statement = withAfters(method, test, statement);
statement = withRules(method, test, statement);
return statement;
}
//
// Statement builders
//
/**
* Returns a {@link Statement} that invokes {@code method} on {@code test}
*/
protected Statement methodInvoker(FrameworkMethod method, Object test) {
return new InvokeMethod(method, test);
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code expecting} attribute, return normally only if {@code next}
* throws an exception of the correct type, and throw an exception
* otherwise.
*/
protected Statement possiblyExpectingExceptions(FrameworkMethod method,
Object test, Statement next) {
Test annotation = method.getAnnotation(Test.class);
return expectsException(annotation) ? new ExpectException(next,
getExpectedException(annotation)) : next;
}
/**
* Returns a {@link Statement}: if {@code method}'s {@code @Test} annotation
* has the {@code timeout} attribute, throw an exception if {@code next}
* takes more than the specified number of milliseconds.
*/
@Deprecated
protected Statement withPotentialTimeout(FrameworkMethod method,
Object test, Statement next) {
long timeout = getTimeout(method.getAnnotation(Test.class));
return timeout > 0 ? new FailOnTimeout(next, timeout) : next;
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @Before}
* methods on this class and superclasses before running {@code next}; if
* any throws an Exception, stop execution and pass the exception on.
*/
protected Statement withBefores(FrameworkMethod method, Object target,
Statement statement) {
List<FrameworkMethod> befores = getTestClass().getAnnotatedMethods(
Before.class);
return befores.isEmpty() ? statement : new RunBefores(statement,
befores, target);
}
/**
* Returns a {@link Statement}: run all non-overridden {@code @After}
* methods on this class and superclasses before running {@code next}; all
* After methods are always executed: exceptions thrown by previous steps
* are combined, if necessary, with exceptions from After methods into a
* {@link MultipleFailureException}.
*/
protected Statement withAfters(FrameworkMethod method, Object target,
Statement statement) {
List<FrameworkMethod> afters = getTestClass().getAnnotatedMethods(
After.class);
return afters.isEmpty() ? statement : new RunAfters(statement, afters,
target);
}
private Statement withRules(FrameworkMethod method, Object target,
Statement statement) {
List<TestRule> testRules = getTestRules(target);
Statement result = statement;
result = withMethodRules(method, testRules, target, result);
result = withTestRules(method, testRules, result);
return result;
}
private Statement withMethodRules(FrameworkMethod method, List<TestRule> testRules,
Object target, Statement result) {
for (org.junit.rules.MethodRule each : getMethodRules(target)) {
if (!testRules.contains(each)) {
result = each.apply(result, method, target);
}
}
return result;
}
private List<org.junit.rules.MethodRule> getMethodRules(Object target) {
return rules(target);
}
/**
* @param target the test case instance
* @return a list of MethodRules that should be applied when executing this
* test
*/
protected List<org.junit.rules.MethodRule> rules(Object target) {
return getTestClass().getAnnotatedFieldValues(target, Rule.class,
org.junit.rules.MethodRule.class);
}
/**
* Returns a {@link Statement}: apply all non-static {@link Value} fields
* annotated with {@link Rule}.
*
* @param statement The base statement
* @return a RunRules statement if any class-level {@link Rule}s are
* found, or the base statement
*/
private Statement withTestRules(FrameworkMethod method, List<TestRule> testRules,
Statement statement) {
return testRules.isEmpty() ? statement :
new RunRules(statement, testRules, describeChild(method));
}
/**
* @param target the test case instance
* @return a list of TestRules that should be applied when executing this
* test
*/
protected List<TestRule> getTestRules(Object target) {
List<TestRule> result = getTestClass().getAnnotatedMethodValues(target,
Rule.class, TestRule.class);
result.addAll(getTestClass().getAnnotatedFieldValues(target,
Rule.class, TestRule.class));
return result;
}
private Class<? extends Throwable> getExpectedException(Test annotation) {
if (annotation == null || annotation.expected() == None.class) {
return null;
} else {
return annotation.expected();
}
}
private boolean expectsException(Test annotation) {
return getExpectedException(annotation) != null;
}
private long getTimeout(Test annotation) {
if (annotation == null) {
return 0;
}
return annotation.timeout();
}
}
Run 1
The code is highly readable and well-structured. It follows a clear, logical organization with methods grouped by purpose (construction, validation, statement building). Nesting is shallow, with only one or two levels in the few conditional blocks. Intent is clearly communicated through descriptive method names and thorough Javadoc comments. The code is concise; each method performs a single, well-defined action. Action granularity is excellent, with one operation per line throughout. Reading flow is natural, as related methods are placed close together and the file proceeds from high-level overview to implementation details. Minor deductions: some lines exceed typical 80-100 character width, and the chain of wrapping 'statement' in methodBlock() could be slightly more explicit, but overall this is exemplary Java code from a readability standpoint.
Run 2
The code is highly readable due to its clear structure and logical organization. Methods are well-named and follow a consistent pattern, such as 'withBefores', 'withAfters', and 'withRules', which makes intent obvious. Nesting is minimal, with most methods flat and using early returns or ternary expressions to avoid deep indentation. Action granularity is generally one action per line, and the reading flow is natural, as methods are grouped into sections like 'Implementation of ParentRunner' and 'Statement builders'. Code length is appropriate—concise but not overly terse. Javadoc comments explain complex logic, especially in methodBlock, without being excessive. Minor issues include a long HashMap declaration line and some deprecated methods, but these do not significantly affect readability.
Run 3
The code demonstrates generally good readability. The class structure follows a clear, logical organization with sections separated by comments, making navigation easy. Methods are well-named and focused on single responsibilities, which enhances clarity of intent. The Javadoc is detailed and helpful for understanding the framework's design. Nesting is minimal - most methods have only one or two levels of indentation. However, there are some readability challenges: certain lines are quite long (especially the ConcurrentHashMap declaration and some method signatures), which hurts the reading flow. The methodBlock() method contains a try-catch with an anonymous inner class that adds complexity. Additionally, the class manages several concerns (validation, description caching, rule handling), which slightly obscures the main execution flow. Overall, it's well-structured code that would benefit primarily from minor formatting improvements like breaking up long lines rather than architectural refactoring.
Run 1
Code Structure: Well-organized into runner operations, validation, and statement builders, with focused helper methods. Nesting: Mostly shallow; the anonymous reflective callable and rule loop introduce only modest nesting. Clarity of intent: Descriptive method names and extensive documentation explain responsibilities, although some comments are misleading, notably withAfters describing execution before the next statement. Code length: Implementation methods are concise, but lengthy documentation, trivial forwarding helpers, and repeated validation patterns increase reading effort. Action granularity: Generally one operation per line, making validation and statement composition easy to follow. Reading flow: Logical section ordering and the sequential methodBlock pipeline provide a natural progression; understanding wrapper execution order, inherited behavior, and interactions between rule types requires additional tracing. Long declarations, inconsistent wrapping, and legacy naming slightly reduce readability.
Run 2
Code Structure: Well organized into runner operations, validation helpers, and statement builders, with focused methods and clear section boundaries. Nesting: Mostly shallow; the anonymous reflective callable and rule loop introduce only modest complexity. Clarity of intent: Descriptive method names and extensive documentation explain the test lifecycle, though some comments are inaccurate or stale, notably withAfters describing execution before the test. Code length: Implementation methods are concise, but lengthy documentation and trivial forwarding helpers increase the overall reading burden. Action granularity: Statements generally perform one action per line, and sequential statement wrapping makes composition explicit. Reading flow: Validation and statement construction follow coherent sequences, although understanding actual execution order requires familiarity with nested Statement wrappers and inherited runner behavior. Long declarations, fully qualified type names, and inconsistent wrapping slightly reduce scanability.
Run 3
Code Structure: Well-organized into runner operations, validation, and statement builders, with focused methods and clear extension points. Nesting: Generally shallow; the anonymous reflective callable and loop with a conditional introduce only modest complexity. Clarity of intent: Descriptive method names and extensive Javadoc explain most behavior, although inaccurate comments (such as withAfters describing execution before the test) and obsolete references reduce trust. Code length: Implementation methods are concise, but lengthy documentation, trivial forwarding helpers, and repeated validation patterns add bulk. Action granularity: Statements generally perform one action per line, and the explicit statement-building pipeline is easy to inspect. Reading flow: Logical grouping and consistent formatting support navigation; understanding decorator execution order, inherited behavior, and interactions between rule types requires additional context. Long generic declarations, fully qualified type names, and occasional awkward line wrapping slightly interrupt scanning.