Home | History | Annotate | Download | only in builders
      1 package org.junit.internal.builders;
      2 
      3 import org.junit.runner.RunWith;
      4 import org.junit.runner.Runner;
      5 import org.junit.runners.model.InitializationError;
      6 import org.junit.runners.model.RunnerBuilder;
      7 
      8 import java.lang.reflect.Modifier;
      9 
     10 
     11 /**
     12  * The {@code AnnotatedBuilder} is a strategy for constructing runners for test class that have been annotated with the
     13  * {@code @RunWith} annotation. All tests within this class will be executed using the runner that was specified within
     14  * the annotation.
     15  * <p>
     16  * If a runner supports inner member classes, the member classes will inherit the runner from the enclosing class, e.g.:
     17  * <pre>
     18  * &#064;RunWith(MyRunner.class)
     19  * public class MyTest {
     20  *     // some tests might go here
     21  *
     22  *     public class MyMemberClass {
     23  *         &#064;Test
     24  *         public void thisTestRunsWith_MyRunner() {
     25  *             // some test logic
     26  *         }
     27  *
     28  *         // some more tests might go here
     29  *     }
     30  *
     31  *     &#064;RunWith(AnotherRunner.class)
     32  *     public class AnotherMemberClass {
     33  *         // some tests might go here
     34  *
     35  *         public class DeepInnerClass {
     36  *             &#064;Test
     37  *             public void thisTestRunsWith_AnotherRunner() {
     38  *                 // some test logic
     39  *             }
     40  *         }
     41  *
     42  *         public class DeepInheritedClass extends SuperTest {
     43  *             &#064;Test
     44  *             public void thisTestRunsWith_SuperRunner() {
     45  *                 // some test logic
     46  *             }
     47  *         }
     48  *     }
     49  * }
     50  *
     51  * &#064;RunWith(SuperRunner.class)
     52  * public class SuperTest {
     53  *     // some tests might go here
     54  * }
     55  * </pre>
     56  * The key points to note here are:
     57  * <ul>
     58  *     <li>If there is no RunWith annotation, no runner will be created.</li>
     59  *     <li>The resolve step is inside-out, e.g. the closest RunWith annotation wins</li>
     60  *     <li>RunWith annotations are inherited and work as if the class was annotated itself.</li>
     61  *     <li>The default JUnit runner does not support inner member classes,
     62  *         so this is only valid for custom runners that support inner member classes.</li>
     63  *     <li>Custom runners with support for inner classes may or may not support RunWith annotations for member
     64  *         classes. Please refer to the custom runner documentation.</li>
     65  * </ul>
     66  *
     67  * @see org.junit.runners.model.RunnerBuilder
     68  * @see org.junit.runner.RunWith
     69  * @since 4.0
     70  */
     71 public class AnnotatedBuilder extends RunnerBuilder {
     72     private static final String CONSTRUCTOR_ERROR_FORMAT = "Custom runner class %s should have a public constructor with signature %s(Class testClass)";
     73 
     74     private final RunnerBuilder suiteBuilder;
     75 
     76     public AnnotatedBuilder(RunnerBuilder suiteBuilder) {
     77         this.suiteBuilder = suiteBuilder;
     78     }
     79 
     80     @Override
     81     public Runner runnerForClass(Class<?> testClass) throws Exception {
     82         for (Class<?> currentTestClass = testClass; currentTestClass != null;
     83              currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) {
     84             RunWith annotation = currentTestClass.getAnnotation(RunWith.class);
     85             if (annotation != null) {
     86                 return buildRunner(annotation.value(), testClass);
     87             }
     88         }
     89 
     90         return null;
     91     }
     92 
     93     private Class<?> getEnclosingClassForNonStaticMemberClass(Class<?> currentTestClass) {
     94         if (currentTestClass.isMemberClass() && !Modifier.isStatic(currentTestClass.getModifiers())) {
     95             return currentTestClass.getEnclosingClass();
     96         } else {
     97             return null;
     98         }
     99     }
    100 
    101     public Runner buildRunner(Class<? extends Runner> runnerClass,
    102             Class<?> testClass) throws Exception {
    103         try {
    104             return runnerClass.getConstructor(Class.class).newInstance(testClass);
    105         } catch (NoSuchMethodException e) {
    106             try {
    107                 return runnerClass.getConstructor(Class.class,
    108                         RunnerBuilder.class).newInstance(testClass, suiteBuilder);
    109             } catch (NoSuchMethodException e2) {
    110                 String simpleName = runnerClass.getSimpleName();
    111                 throw new InitializationError(String.format(
    112                         CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName));
    113             }
    114         }
    115     }
    116 }