001package org.junit.rules; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.List; 006 007import org.junit.Rule; 008import org.junit.runner.Description; 009import 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 */ 058public 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}