Coverage Report - org.jbehave.core.steps.Steps
 
Classes in this File Line Coverage Branch Coverage Complexity
Steps
99%
99/100
97%
41/42
2
Steps$DuplicateCandidateFound
100%
2/2
N/A
2
 
 1  
 package org.jbehave.core.steps;
 2  
 
 3  
 import static java.util.Arrays.asList;
 4  
 import static org.jbehave.core.annotations.AfterScenario.Outcome.ANY;
 5  
 import static org.jbehave.core.annotations.AfterScenario.Outcome.FAILURE;
 6  
 import static org.jbehave.core.annotations.AfterScenario.Outcome.SUCCESS;
 7  
 import static org.jbehave.core.steps.StepType.GIVEN;
 8  
 import static org.jbehave.core.steps.StepType.THEN;
 9  
 import static org.jbehave.core.steps.StepType.WHEN;
 10  
 
 11  
 import java.lang.annotation.Annotation;
 12  
 import java.lang.reflect.Method;
 13  
 import java.util.ArrayList;
 14  
 import java.util.List;
 15  
 
 16  
 import org.apache.commons.lang.builder.ToStringBuilder;
 17  
 import org.apache.commons.lang.builder.ToStringStyle;
 18  
 import org.jbehave.core.annotations.AfterStories;
 19  
 import org.jbehave.core.annotations.AfterScenario;
 20  
 import org.jbehave.core.annotations.AfterStory;
 21  
 import org.jbehave.core.annotations.Alias;
 22  
 import org.jbehave.core.annotations.Aliases;
 23  
 import org.jbehave.core.annotations.BeforeStories;
 24  
 import org.jbehave.core.annotations.BeforeScenario;
 25  
 import org.jbehave.core.annotations.BeforeStory;
 26  
 import org.jbehave.core.annotations.Given;
 27  
 import org.jbehave.core.annotations.Then;
 28  
 import org.jbehave.core.annotations.When;
 29  
 import org.jbehave.core.annotations.AfterScenario.Outcome;
 30  
 import org.jbehave.core.configuration.Configuration;
 31  
 import org.jbehave.core.configuration.MostUsefulConfiguration;
 32  
 import org.jbehave.core.parsers.RegexPrefixCapturingPatternParser;
 33  
 import org.jbehave.core.parsers.StepPatternParser;
 34  
 import org.jbehave.core.steps.StepCollector.Stage;
 35  
 
 36  
 /**
 37  
  * <p>
 38  
  * Default implementation of {@link CandidateSteps} which provides the step
 39  
  * candidates that match the steps being run.
 40  
  * </p>
 41  
  * <p>
 42  
  * To provide your step candidate methods, you can either pass it any
 43  
  * {@link Object} instance that it can wrap ("has-a" relationship) or extend the
 44  
  * {@link Steps} class ("is-a" relationship), in which case the instance is the
 45  
  * extended {@link Steps} class itself. <b>The "has-a" design model is strongly
 46  
  * recommended as it does not have tie-ins in the {@link Steps} class
 47  
  * implementation</b>.
 48  
  * </p>
 49  
  * <p>
 50  
  * You can define the methods that should be run by annotating them with
 51  
  * {@link Given @Given}, {@link When @When} or {@link Then @Then}, and providing
 52  
  * as a value for each annotation a pattern matches the textual step. The value
 53  
  * is interpreted by the {@link StepPatternParser}, which by default is a
 54  
  * {@link RegexPrefixCapturingPatternParser} that interprets the words starting
 55  
  * with '$' as parameters.
 56  
  * </p>
 57  
  * <p>
 58  
  * For instance, you could define a method as:
 59  
  * 
 60  
  * <pre>
 61  
  * @When("I log in as $username with password: $password")
 62  
  * public void logIn(String username, String password) { //... }
 63  
  * </pre>
 64  
  * 
 65  
  * and this would match the step:
 66  
  * 
 67  
  * <pre>
 68  
  * When I log in as Liz with password: Pa55word
 69  
  * </pre>
 70  
  * 
 71  
  * </p>
 72  
  * <p>
 73  
  * When the step is performed, the parameters matched will be passed to the
 74  
  * method, so in this case the effect will be to invoke:
 75  
  * </p>
 76  
  * 
 77  
  * <pre>
 78  
  * logIn(&quot;Liz&quot;, &quot;Pa55word&quot;);
 79  
  * </pre>
 80  
  * <p>
 81  
  * The {@link Configuration} can be used to provide customize the
 82  
  * {@link StepCandidate}s that are created, e.g. providing a step monitor or
 83  
  * creating them in "dry run" mode.
 84  
  * </p>
 85  
  */
 86  
 public class Steps implements CandidateSteps {
 87  
 
 88  
     private final Configuration configuration;
 89  
     private final Object instance;
 90  
 
 91  
     /**
 92  
      * Creates Steps with default configuration for a class extending this
 93  
      * instance and containing the candidate step methods
 94  
      */
 95  
     public Steps() {
 96  51
         this(new MostUsefulConfiguration());
 97  51
     }
 98  
 
 99  
     /**
 100  
      * Creates Steps with given custom configuration for a class extending this
 101  
      * instance and containing the candidate step methods
 102  
      * 
 103  
      * @param configuration
 104  
      *            the Configuration
 105  
      */
 106  
     public Steps(Configuration configuration) {
 107  58
         this(configuration, null);
 108  58
     }
 109  
 
 110  
     /**
 111  
      * Creates Steps with given custom configuration wrapping an Object instance
 112  
      * containing the candidate step methods
 113  
      * 
 114  
      * @param configuration
 115  
      *            the Configuration
 116  
      * @param instance
 117  
      *            the Object instance
 118  
      */
 119  70
     public Steps(Configuration configuration, Object instance) {
 120  70
         this.configuration = configuration;
 121  70
         this.instance = instance;
 122  70
     }
 123  
 
 124  
     public Object instance() {
 125  127
         if (instance == null) {
 126  100
             return this;
 127  
         }
 128  27
         return instance;
 129  
     }
 130  
 
 131  
     public Configuration configuration() {
 132  12
         return configuration;
 133  
     }
 134  
 
 135  
     public List<StepCandidate> listCandidates() {
 136  14
         List<StepCandidate> candidates = new ArrayList<StepCandidate>();
 137  14
         for (Method method : allMethods()) {
 138  229
             if (method.isAnnotationPresent(Given.class)) {
 139  14
                 Given annotation = method.getAnnotation(Given.class);
 140  14
                 String value = annotation.value();
 141  14
                 int priority = annotation.priority();
 142  14
                 addCandidate(candidates, method, GIVEN, value, priority);
 143  13
                 addCandidatesFromAliases(candidates, method, GIVEN, priority);
 144  
             }
 145  228
             if (method.isAnnotationPresent(When.class)) {
 146  13
                 When annotation = method.getAnnotation(When.class);
 147  13
                 String value = annotation.value();
 148  13
                 int priority = annotation.priority();
 149  13
                 addCandidate(candidates, method, WHEN, value, priority);
 150  13
                 addCandidatesFromAliases(candidates, method, WHEN, priority);
 151  
             }
 152  228
             if (method.isAnnotationPresent(Then.class)) {
 153  9
                 Then annotation = method.getAnnotation(Then.class);
 154  9
                 String value = annotation.value();
 155  9
                 int priority = annotation.priority();
 156  9
                 addCandidate(candidates, method, THEN, value, priority);
 157  9
                 addCandidatesFromAliases(candidates, method, THEN, priority);
 158  228
             }
 159  
         }
 160  13
         return candidates;
 161  
     }
 162  
 
 163  
     private void addCandidate(List<StepCandidate> candidates, Method method, StepType stepType,
 164  
             String stepPatternAsString, int priority) {
 165  48
         checkForDuplicateCandidates(candidates, stepType, stepPatternAsString);
 166  47
         StepCandidate step = createCandidate(method, stepType, stepPatternAsString, priority, configuration);
 167  47
         step.useStepMonitor(configuration.stepMonitor());
 168  47
         step.useParanamer(configuration.paranamer());
 169  47
         step.doDryRun(configuration.dryRun());
 170  47
         candidates.add(step);
 171  47
     }
 172  
 
 173  
     private void checkForDuplicateCandidates(List<StepCandidate> candidates, StepType stepType, String patternAsString) {
 174  48
         for (StepCandidate candidate : candidates) {
 175  88
             if (candidate.getStepType() == stepType && candidate.getPatternAsString().equals(patternAsString)) {
 176  1
                 throw new DuplicateCandidateFound(stepType, patternAsString);
 177  
             }
 178  
         }
 179  47
     }
 180  
 
 181  
     private void addCandidatesFromAliases(List<StepCandidate> candidates, Method method, StepType stepType, int priority) {
 182  35
         if (method.isAnnotationPresent(Aliases.class)) {
 183  3
             String[] aliases = method.getAnnotation(Aliases.class).values();
 184  9
             for (String alias : aliases) {
 185  6
                 addCandidate(candidates, method, stepType, alias, priority);
 186  
             }
 187  
         }
 188  35
         if (method.isAnnotationPresent(Alias.class)) {
 189  6
             String alias = method.getAnnotation(Alias.class).value();
 190  6
             addCandidate(candidates, method, stepType, alias, priority);
 191  
         }
 192  35
     }
 193  
 
 194  
     private StepCandidate createCandidate(Method method, StepType stepType, String stepPatternAsString, int priority,
 195  
             Configuration configuration) {
 196  47
         return new StepCandidate(stepPatternAsString, priority, stepType, method, instance(), configuration.keywords()
 197  
                 .startingWordsByType(), configuration.stepPatternParser(), configuration.parameterConverters());
 198  
     }
 199  
 
 200  
     public List<BeforeOrAfterStep> listBeforeOrAfterStories() {
 201  1
         List<BeforeOrAfterStep> steps = new ArrayList<BeforeOrAfterStep>();
 202  1
         steps.addAll(stepsHaving(Stage.BEFORE, BeforeStories.class));
 203  1
         steps.addAll(stepsHaving(Stage.AFTER, AfterStories.class));
 204  1
         return steps;
 205  
     }
 206  
 
 207  
     public List<BeforeOrAfterStep> listBeforeOrAfterStory(boolean givenStory) {
 208  3
         List<BeforeOrAfterStep> steps = new ArrayList<BeforeOrAfterStep>();
 209  3
         steps.addAll(stepsHaving(Stage.BEFORE, BeforeStory.class, givenStory));
 210  3
         steps.addAll(stepsHaving(Stage.AFTER, AfterStory.class, givenStory));
 211  3
         return steps;
 212  
     }
 213  
 
 214  
     public List<BeforeOrAfterStep> listBeforeOrAfterScenario() {
 215  3
         List<BeforeOrAfterStep> steps = new ArrayList<BeforeOrAfterStep>();
 216  3
         steps.addAll(stepsHaving(Stage.BEFORE, BeforeScenario.class));
 217  3
         steps.addAll(stepsHaving(Stage.AFTER, AfterScenario.class, ANY));
 218  3
         steps.addAll(stepsHaving(Stage.AFTER, AfterScenario.class, SUCCESS));
 219  3
         steps.addAll(stepsHaving(Stage.AFTER, AfterScenario.class, FAILURE));
 220  3
         return steps;
 221  
     }
 222  
 
 223  
     private List<BeforeOrAfterStep> stepsHaving(Stage stage, Class<? extends Annotation> annotationClass,
 224  
             boolean givenStory) {
 225  6
         List<BeforeOrAfterStep> steps = new ArrayList<BeforeOrAfterStep>();
 226  6
         for (final Method method : annotatatedMethods(annotationClass)) {
 227  12
             if (runnableStoryStep(method.getAnnotation(annotationClass), givenStory)) {
 228  6
                 steps.add(createBeforeOrAfterStep(stage, method));
 229  
             }
 230  
         }
 231  6
         return steps;
 232  
     }
 233  
 
 234  
     private boolean runnableStoryStep(Annotation annotation, boolean givenStory) {
 235  12
         boolean uponGivenStory = uponGivenStory(annotation);
 236  12
         return uponGivenStory == givenStory;
 237  
     }
 238  
 
 239  
     private boolean uponGivenStory(Annotation annotation) {
 240  12
         if (annotation instanceof BeforeStory) {
 241  6
             return ((BeforeStory) annotation).uponGivenStory();
 242  6
         } else if (annotation instanceof AfterStory) {
 243  6
             return ((AfterStory) annotation).uponGivenStory();
 244  
         }
 245  0
         return false;
 246  
     }
 247  
 
 248  
     private List<BeforeOrAfterStep> stepsHaving(Stage stage, Class<? extends Annotation> annotationClass) {
 249  5
         List<BeforeOrAfterStep> steps = new ArrayList<BeforeOrAfterStep>();
 250  5
         for (Method method : annotatatedMethods(annotationClass)) {
 251  6
             steps.add(createBeforeOrAfterStep(stage, method));
 252  
         }
 253  5
         return steps;
 254  
     }
 255  
 
 256  
     private List<BeforeOrAfterStep> stepsHaving(Stage stage, Class<? extends AfterScenario> annotationClass,
 257  
             Outcome outcome) {
 258  9
         List<BeforeOrAfterStep> steps = new ArrayList<BeforeOrAfterStep>();
 259  9
         for (Method method : annotatatedMethods(annotationClass)) {
 260  18
             AfterScenario annotation = method.getAnnotation(annotationClass);
 261  18
             if (outcome.equals(annotation.uponOutcome())) {
 262  6
                 steps.add(createBeforeOrAfterStep(stage, method, outcome));
 263  
             }
 264  18
         }
 265  9
         return steps;
 266  
     }
 267  
 
 268  
     private BeforeOrAfterStep createBeforeOrAfterStep(Stage stage, Method method) {
 269  12
         return new BeforeOrAfterStep(stage, method, instance());
 270  
     }
 271  
 
 272  
     private BeforeOrAfterStep createBeforeOrAfterStep(Stage stage, Method method, Outcome outcome) {
 273  6
         return new BeforeOrAfterStep(stage, method, instance(), outcome);
 274  
     }
 275  
 
 276  
     private List<Method> allMethods() {
 277  34
         return asList(instance().getClass().getMethods());
 278  
     }
 279  
 
 280  
     private List<Method> annotatatedMethods(Class<? extends Annotation> annotationClass) {
 281  20
         List<Method> annotated = new ArrayList<Method>();
 282  20
         for (Method method : allMethods()) {
 283  516
             if (method.isAnnotationPresent(annotationClass)) {
 284  36
                 annotated.add(method);
 285  
             }
 286  
         }
 287  20
         return annotated;
 288  
     }
 289  
 
 290  
     @SuppressWarnings("serial")
 291  
     public static class DuplicateCandidateFound extends RuntimeException {
 292  
 
 293  
         public DuplicateCandidateFound(StepType stepType, String patternAsString) {
 294  1
             super(stepType + " " + patternAsString);
 295  1
         }
 296  
 
 297  
     }
 298  
 
 299  
     @Override
 300  
     public String toString() {
 301  9
         return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append(instance()).toString();
 302  
     }
 303  
 
 304  
 }