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