• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AnnotatedBuilder(RunnerBuilder suiteBuilder)76     public AnnotatedBuilder(RunnerBuilder suiteBuilder) {
77         this.suiteBuilder = suiteBuilder;
78     }
79 
80     @Override
runnerForClass(Class<?> testClass)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 
getEnclosingClassForNonStaticMemberClass(Class<?> currentTestClass)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 
buildRunner(Class<? extends Runner> runnerClass, Class<?> testClass)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 }