diff options
Diffstat (limited to 'src/main/java/org/junit/internal/builders/AnnotatedBuilder.java')
-rw-r--r-- | src/main/java/org/junit/internal/builders/AnnotatedBuilder.java | 143 |
1 files changed, 107 insertions, 36 deletions
diff --git a/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java b/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java index 8ed9ca7..04d7a68 100644 --- a/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java +++ b/src/main/java/org/junit/internal/builders/AnnotatedBuilder.java @@ -1,6 +1,3 @@ -/** - * - */ package org.junit.internal.builders; import org.junit.runner.RunWith; @@ -8,38 +5,112 @@ import org.junit.runner.Runner; import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; +import java.lang.reflect.Modifier; + + +/** + * The {@code AnnotatedBuilder} is a strategy for constructing runners for test class that have been annotated with the + * {@code @RunWith} annotation. All tests within this class will be executed using the runner that was specified within + * the annotation. + * <p> + * If a runner supports inner member classes, the member classes will inherit the runner from the enclosing class, e.g.: + * <pre> + * @RunWith(MyRunner.class) + * public class MyTest { + * // some tests might go here + * + * public class MyMemberClass { + * @Test + * public void thisTestRunsWith_MyRunner() { + * // some test logic + * } + * + * // some more tests might go here + * } + * + * @RunWith(AnotherRunner.class) + * public class AnotherMemberClass { + * // some tests might go here + * + * public class DeepInnerClass { + * @Test + * public void thisTestRunsWith_AnotherRunner() { + * // some test logic + * } + * } + * + * public class DeepInheritedClass extends SuperTest { + * @Test + * public void thisTestRunsWith_SuperRunner() { + * // some test logic + * } + * } + * } + * } + * + * @RunWith(SuperRunner.class) + * public class SuperTest { + * // some tests might go here + * } + * </pre> + * The key points to note here are: + * <ul> + * <li>If there is no RunWith annotation, no runner will be created.</li> + * <li>The resolve step is inside-out, e.g. the closest RunWith annotation wins</li> + * <li>RunWith annotations are inherited and work as if the class was annotated itself.</li> + * <li>The default JUnit runner does not support inner member classes, + * so this is only valid for custom runners that support inner member classes.</li> + * <li>Custom runners with support for inner classes may or may not support RunWith annotations for member + * classes. Please refer to the custom runner documentation.</li> + * </ul> + * + * @see org.junit.runners.model.RunnerBuilder + * @see org.junit.runner.RunWith + * @since 4.0 + */ public class AnnotatedBuilder extends RunnerBuilder { - private static final String CONSTRUCTOR_ERROR_FORMAT= "Custom runner class %s should have a public constructor with signature %s(Class testClass)"; - - private RunnerBuilder fSuiteBuilder; - - public AnnotatedBuilder(RunnerBuilder suiteBuilder) { - fSuiteBuilder= suiteBuilder; - } - - @Override - public Runner runnerForClass(Class<?> testClass) throws Exception { - RunWith annotation= testClass.getAnnotation(RunWith.class); - if (annotation != null) - return buildRunner(annotation.value(), testClass); - return null; - } - - public Runner buildRunner(Class<? extends Runner> runnerClass, - Class<?> testClass) throws Exception { - try { - return runnerClass.getConstructor(Class.class).newInstance( - new Object[] { testClass }); - } catch (NoSuchMethodException e) { - try { - return runnerClass.getConstructor(Class.class, - RunnerBuilder.class).newInstance( - new Object[] { testClass, fSuiteBuilder }); - } catch (NoSuchMethodException e2) { - String simpleName= runnerClass.getSimpleName(); - throw new InitializationError(String.format( - CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName)); - } - } - } + private static final String CONSTRUCTOR_ERROR_FORMAT = "Custom runner class %s should have a public constructor with signature %s(Class testClass)"; + + private final RunnerBuilder suiteBuilder; + + public AnnotatedBuilder(RunnerBuilder suiteBuilder) { + this.suiteBuilder = suiteBuilder; + } + + @Override + public Runner runnerForClass(Class<?> testClass) throws Exception { + for (Class<?> currentTestClass = testClass; currentTestClass != null; + currentTestClass = getEnclosingClassForNonStaticMemberClass(currentTestClass)) { + RunWith annotation = currentTestClass.getAnnotation(RunWith.class); + if (annotation != null) { + return buildRunner(annotation.value(), testClass); + } + } + + return null; + } + + private Class<?> getEnclosingClassForNonStaticMemberClass(Class<?> currentTestClass) { + if (currentTestClass.isMemberClass() && !Modifier.isStatic(currentTestClass.getModifiers())) { + return currentTestClass.getEnclosingClass(); + } else { + return null; + } + } + + public Runner buildRunner(Class<? extends Runner> runnerClass, + Class<?> testClass) throws Exception { + try { + return runnerClass.getConstructor(Class.class).newInstance(testClass); + } catch (NoSuchMethodException e) { + try { + return runnerClass.getConstructor(Class.class, + RunnerBuilder.class).newInstance(testClass, suiteBuilder); + } catch (NoSuchMethodException e2) { + String simpleName = runnerClass.getSimpleName(); + throw new InitializationError(String.format( + CONSTRUCTOR_ERROR_FORMAT, simpleName, simpleName)); + } + } + } }
\ No newline at end of file |