All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.junit.runners.BlockJUnit4ClassRunner Maven / Gradle / Ivy

Go to download

JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.

There is a newer version: 4.13.2
Show newest version
package org.junit.runners;

import static org.junit.internal.runners.rules.RuleMemberValidator.RULE_METHOD_VALIDATOR;
import static org.junit.internal.runners.rules.RuleMemberValidator.RULE_VALIDATOR;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

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.MethodRule;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.model.FrameworkMember;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.MemberValueConsumer;
import org.junit.runners.model.MultipleFailureException;
import org.junit.runners.model.Statement;
import org.junit.runners.model.TestClass;
import org.junit.validator.PublicClassValidator;
import org.junit.validator.TestClassValidator;

/**
 * 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: * *

    *
  • 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. * *
  • It is published, and extension and reuse are encouraged, whereas {@code * JUnit4ClassRunner} was in an internal package, and is now deprecated. *
*

* 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 { private static TestClassValidator PUBLIC_CLASS_VALIDATOR = new PublicClassValidator(); private final ConcurrentMap methodDescriptions = new ConcurrentHashMap(); /** * Creates a BlockJUnit4ClassRunner to run {@code testClass} * * @throws InitializationError if the test class is malformed. */ public BlockJUnit4ClassRunner(Class testClass) throws InitializationError { super(testClass); } /** * Creates a BlockJUnit4ClassRunner to run {@code testClass}. * * @throws InitializationError if the test class is malformed. * @since 4.13 */ protected BlockJUnit4ClassRunner(TestClass testClass) throws InitializationError { super(testClass); } // // Implementation of ParentRunner // @Override protected void runChild(final FrameworkMethod method, RunNotifier notifier) { Description description = describeChild(method); if (isIgnored(method)) { notifier.fireTestIgnored(description); } else { Statement statement = new Statement() { @Override public void evaluate() throws Throwable { methodBlock(method).evaluate(); } }; runLeaf(statement, 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 = methodDescriptions.get(method); if (description == null) { description = Description.createTestDescription(getTestClass().getJavaClass(), testName(method), method.getAnnotations()); methodDescriptions.putIfAbsent(method, description); } return description; } @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); validatePublicConstructor(errors); validateNoNonStaticInnerClass(errors); validateConstructor(errors); validateInstanceMethods(errors); validateFields(errors); validateMethods(errors); } private void validatePublicConstructor(List errors) { if (getTestClass().getJavaClass() != null) { errors.addAll(PUBLIC_CLASS_VALIDATOR.validateTestClass(getTestClass())); } } 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 */ @Deprecated protected void validateInstanceMethods(List errors) { validatePublicVoidNoArgMethods(After.class, false, errors); validatePublicVoidNoArgMethods(Before.class, false, errors); validateTestMethods(errors); if (computeTestMethods().isEmpty()) { errors.add(new Exception("No runnable methods")); } } protected void validateFields(List errors) { RULE_VALIDATOR.validate(getTestClass(), errors); } private void validateMethods(List 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 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 a new fixture to run a particular test {@code method} against. * Default implementation executes the no-argument {@link #createTest()} method. * * @since 4.13 */ protected Object createTest(FrameworkMethod method) throws Exception { return createTest(); } /** * 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 {@link #createTest(org.junit.runners.model.FrameworkMethod)}, and * throw any exceptions thrown by either operation. *
  • HOWEVER, if {@code method}'s {@code @Test} annotation has the {@link Test#expected()} * 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(final FrameworkMethod method) { Object test; try { test = new ReflectiveCallable() { @Override protected Object runReflectiveCall() throws Throwable { return createTest(method); } }.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); statement = withInterruptIsolation(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 {@link Test#expected()} 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); Class expectedExceptionClass = getExpectedException(annotation); return expectedExceptionClass != null ? new ExpectException(next, expectedExceptionClass) : 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 */ @Deprecated protected Statement withPotentialTimeout(FrameworkMethod method, Object test, Statement next) { long timeout = getTimeout(method.getAnnotation(Test.class)); if (timeout <= 0) { return next; } return FailOnTimeout.builder() .withTimeout(timeout, TimeUnit.MILLISECONDS) .build(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 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 afters = getTestClass().getAnnotatedMethods( After.class); return afters.isEmpty() ? statement : new RunAfters(statement, afters, target); } private Statement withRules(FrameworkMethod method, Object target, Statement statement) { RuleContainer ruleContainer = new RuleContainer(); CURRENT_RULE_CONTAINER.set(ruleContainer); try { List testRules = getTestRules(target); for (MethodRule each : rules(target)) { if (!(each instanceof TestRule && testRules.contains(each))) { ruleContainer.add(each); } } for (TestRule rule : testRules) { ruleContainer.add(rule); } } finally { CURRENT_RULE_CONTAINER.remove(); } return ruleContainer.apply(method, describeChild(method), target, statement); } /** * @param target the test case instance * @return a list of MethodRules that should be applied when executing this * test */ protected List rules(Object target) { RuleCollector collector = new RuleCollector(); getTestClass().collectAnnotatedMethodValues(target, Rule.class, MethodRule.class, collector); getTestClass().collectAnnotatedFieldValues(target, Rule.class, MethodRule.class, collector); return collector.result; } /** * @param target the test case instance * @return a list of TestRules that should be applied when executing this * test */ protected List getTestRules(Object target) { RuleCollector collector = new RuleCollector(); getTestClass().collectAnnotatedMethodValues(target, Rule.class, TestRule.class, collector); getTestClass().collectAnnotatedFieldValues(target, Rule.class, TestRule.class, collector); return collector.result; } private Class getExpectedException(Test annotation) { if (annotation == null || annotation.expected() == None.class) { return null; } else { return annotation.expected(); } } private long getTimeout(Test annotation) { if (annotation == null) { return 0; } return annotation.timeout(); } private static final ThreadLocal CURRENT_RULE_CONTAINER = new ThreadLocal(); private static class RuleCollector implements MemberValueConsumer { final List result = new ArrayList(); public void accept(FrameworkMember member, T value) { Rule rule = member.getAnnotation(Rule.class); if (rule != null) { RuleContainer container = CURRENT_RULE_CONTAINER.get(); if (container != null) { container.setOrder(value, rule.order()); } } result.add(value); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy