001 package org.junit.rules;
002
003 import java.util.ArrayList;
004 import java.util.Collections;
005 import java.util.List;
006
007 import org.junit.Rule;
008 import org.junit.runner.Description;
009 import org.junit.runners.model.Statement;
010
011 /**
012 * The {@code RuleChain} can be used for creating composite rules. You create a
013 * {@code RuleChain} with {@link #outerRule(TestRule)} and subsequent calls of
014 * {@link #around(TestRule)}:
015 *
016 * <pre>
017 * public abstract class CompositeRules {
018 * public static TestRule extendedLogging() {
019 * return RuleChain.outerRule(new LoggingRule("outer rule"))
020 * .around(new LoggingRule("middle rule"))
021 * .around(new LoggingRule("inner rule"));
022 * }
023 * }
024 * </pre>
025 *
026 * <pre>
027 * public class UseRuleChain {
028 * @Rule
029 * public final TestRule extendedLogging = CompositeRules.extendedLogging();
030 *
031 * @Test
032 * public void example() {
033 * assertTrue(true);
034 * }
035 * }
036 * </pre>
037 *
038 * writes the log
039 *
040 * <pre>
041 * starting outer rule
042 * starting middle rule
043 * starting inner rule
044 * finished inner rule
045 * finished middle rule
046 * finished outer rule
047 * </pre>
048 *
049 * In older versions of JUnit (before 4.13) {@code RuleChain} was used for
050 * ordering rules. We recommend to not use it for this purpose anymore. You can
051 * use the attribute {@code order} of the annotation {@link Rule#order() Rule}
052 * or {@link org.junit.ClassRule#order() ClassRule} for ordering rules.
053 *
054 * @see org.junit.Rule#order()
055 * @see org.junit.ClassRule#order()
056 * @since 4.10
057 */
058 public class RuleChain implements TestRule {
059 private static final RuleChain EMPTY_CHAIN = new RuleChain(
060 Collections.<TestRule>emptyList());
061
062 private List<TestRule> rulesStartingWithInnerMost;
063
064 /**
065 * Returns a {@code RuleChain} without a {@link TestRule}. This method may
066 * be the starting point of a {@code RuleChain}.
067 *
068 * @return a {@code RuleChain} without a {@link TestRule}.
069 */
070 public static RuleChain emptyRuleChain() {
071 return EMPTY_CHAIN;
072 }
073
074 /**
075 * Returns a {@code RuleChain} with a single {@link TestRule}. This method
076 * is the usual starting point of a {@code RuleChain}.
077 *
078 * @param outerRule the outer rule of the {@code RuleChain}.
079 * @return a {@code RuleChain} with a single {@link TestRule}.
080 */
081 public static RuleChain outerRule(TestRule outerRule) {
082 return emptyRuleChain().around(outerRule);
083 }
084
085 private RuleChain(List<TestRule> rules) {
086 this.rulesStartingWithInnerMost = rules;
087 }
088
089 /**
090 * Create a new {@code RuleChain}, which encloses the given {@link TestRule} with
091 * the rules of the current {@code RuleChain}.
092 *
093 * @param enclosedRule the rule to enclose; must not be {@code null}.
094 * @return a new {@code RuleChain}.
095 * @throws NullPointerException if the argument {@code enclosedRule} is {@code null}
096 */
097 public RuleChain around(TestRule enclosedRule) {
098 if (enclosedRule == null) {
099 throw new NullPointerException("The enclosed rule must not be null");
100 }
101 List<TestRule> rulesOfNewChain = new ArrayList<TestRule>();
102 rulesOfNewChain.add(enclosedRule);
103 rulesOfNewChain.addAll(rulesStartingWithInnerMost);
104 return new RuleChain(rulesOfNewChain);
105 }
106
107 /**
108 * {@inheritDoc}
109 */
110 public Statement apply(Statement base, Description description) {
111 return new RunRules(base, rulesStartingWithInnerMost, description);
112 }
113 }