package org.junit.runners; import static org.junit.internal.runners.rules.RuleFieldValidator.RULE_VALIDATOR; import java.util.List; 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}). * * BlockJUnit4ClassRunner has advantages for writers of custom JUnit runners * that are slight changes to the default behavior, however: * * */ public class BlockJUnit4ClassRunner extends ParentRunner { /** * 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 (method.getAnnotation(Ignore.class) != null) { notifier.fireTestIgnored(description); } else { runLeaf(methodBlock(method), description, notifier); } } @Override protected Description describeChild(FrameworkMethod method) { return Description.createTestDescription(getTestClass().getJavaClass(), testName(method), method.getAnnotations()); } @Override protected List 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 computeTestMethods() { return getTestClass().getAnnotatedMethods(Test.class); } @Override protected void collectInitializationErrors(List errors) { super.collectInitializationErrors(errors); validateNoNonStaticInnerClass(errors); validateConstructor(errors); validateInstanceMethods(errors); validateFields(errors); } protected void validateNoNonStaticInnerClass(List 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 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 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 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 unused API, will go away in future version */ @Deprecated protected void validateInstanceMethods(List errors) { validatePublicVoidNoArgMethods(After.class, false, errors); validatePublicVoidNoArgMethods(Before.class, false, errors); validateTestMethods(errors); if (computeTestMethods().size() == 0) errors.add(new Exception("No runnable methods")); } private void validateFields(List errors) { RULE_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 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: * *
    *
  • Invoke {@code method} on the result of {@code createTest()}, and * throw any exceptions thrown by either operation. *
  • 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. *
  • 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. *
  • 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. *
  • 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}. *
  • 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} *
* * 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. * * @deprecated Will be private soon: use Rules instead */ @Deprecated 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 Will be private soon: use Rules instead */ @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. * * @deprecated Will be private soon: use Rules instead */ @Deprecated protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) { List 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}. * * @deprecated Will be private soon: use Rules instead */ @Deprecated protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) { List afters= getTestClass().getAnnotatedMethods( After.class); return afters.isEmpty() ? statement : new RunAfters(statement, afters, target); } private Statement withRules(FrameworkMethod method, Object target, Statement statement) { Statement result= statement; result= withMethodRules(method, target, result); result= withTestRules(method, target, result); return result; } @SuppressWarnings("deprecation") private Statement withMethodRules(FrameworkMethod method, Object target, Statement result) { List testRules= getTestRules(target); for (org.junit.rules.MethodRule each : getMethodRules(target)) if (! testRules.contains(each)) result= each.apply(result, method, target); return result; } @SuppressWarnings("deprecation") private List 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 * @deprecated {@link org.junit.rules.MethodRule} is a deprecated interface. Port to * {@link TestRule} and * {@link BlockJUnit4ClassRunner#getTestRules(Object)} */ @Deprecated protected List 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, Object target, Statement statement) { List testRules= getTestRules(target); 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 getTestRules(Object target) { return getTestClass().getAnnotatedFieldValues(target, Rule.class, TestRule.class); } private Class 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(); } }