Cucumber JVM: More Scenarios

In my previous post I started to play around with cucumber-jvm to see how it could be used for Java development. Last time I finished with one scenario and now I’m going to move forward with my simple text munger implementation and see how to use cucumber-jvm with multiple scenarios.

Before doing anything, I installed infinitest so that I didn’t have run the test cases manually. This really sped up the development. I recommend you to do the same.

I filled out my steps, did a bit of implementation and now this is how the steps look like:

Quick Detour - Playing Around With Step Definitions

The ruby version of cucumber is quite flexible. For example, Given, When and Then are not bound to the step definitions, so the following works - simple_text_munger.feature:

Feature: simple text munger kata

Scenario: Do nothing with a two-letter word
   Then I have an instance of my class
  Given I call my method with "an"
   When I receive "an"

The good news is that it also works with cucumber-jvm, and there is more! Sometimes you have to give multiple steps with the same Given, When and Then prefix. In this case you can use the And prefix. So instead of multiple Thens in simple_text_munger.feature:

Feature: simple text munger kata

Scenario: Do nothing with a two-letter word
   Then I have an instance of my class
   Then I call my method with "an"
   Then I receive "an"

Use one Then with several Ands:

Feature: simple text munger kata

Scenario: Do nothing with a two-letter word
   Then I have an instance of my class
    And I call my method with "an"
    And I receive "an"

These formats are ugly and I don’t recommend using them, but the goal of my posts is to show the capabilities of cucumber-jvm, and so far it has all the features the ruby version has.

The Next Scenarios

It’s time to return to the subject at hand and move forward with my implementation. My modified text munger kata says that* the first and the last letter of a word should be kept and the letters in between need to be reversed.* I have one scenario so far, where I check how my implementation behaves with a two-letter word. So let’s have two more scenarios in the simple_text_munger.feature:

Feature: simple text munger kata

Scenario: Do nothing with a one-letter word
  Given I have an instance of my class
   When I call my method with "a"
   Then I receive "a"

Scenario: Do nothing with a two-letter word
  Given I have an instance of my class
   When I call my method with "an"
   Then I receive "an"

Scenario: Do nothing with a three-letter word
  Given I have an instance of my class
   When I call my method with "and"
   Then I receive "and"

Nothing else needs to be changed, because I’m still all green:

Refactoring the Feature File

If you check the scenarios above, they are almost the same, only the in and out parameters differ. This is not DRY, so let’s refactor a little bit. After moving the first step into Background - which is executed before each scenario -, our .feature file - simple_text_munger.feature - looks like this:

Feature: simple text munger kata

  Background:
    Given I have an instance of my class

  Scenario: Do nothing with a one-letter word
     When I call my method with "a"
     Then I receive "a"

  Scenario: Do nothing with a two-letter word
     When I call my method with "an"
     Then I receive "an"

  Scenario: Do nothing with a three-letter word
     When I call my method with "and"
     Then I receive "and"

Looks a bit better, but I’m still not one hundred percent satisfied: the scenarios are the same except the parameters they have. The ruby version of cucumber has a very powerful feature called Scenario Outline. This is how I would refactor the scenarios above using this feature:

Feature: simple text munger kata

  Background:
    Given I have an instance of my class

  Scenario Outline: Do nothing with words shorter than 4 letters
    When I call my method with <input>
    Then I receive <output>

    Examples:
      | input | output |
      | "a"   | "a"    |
      | "an"  | "an"   |
      | "and" | "and"  |

And this works just as well with cucumber-jvm:

The table structure from above is also commonly used like this:

Scenario: do something interesting
    Given the following users
      | first_name | last_name |
      | John       | Doe       |
      | Jane       | Doe       |

And the matching step implementation in Java:

package com.zsoltfabok.blog;

import java.util.List;

import cucumber.annotation.en.Given;

public class SomeOtherStepsdef {

    @Given("^the following users$")
    public void the_following_users(List<entry> entries) {
        // iterate through the list and do something
    }

    class Entry {
        String first_name;
        String last_name;
    }
}

Fortunately, it works just as well with cucumber-jvm. I don’t see a chance to use it with the current example, but I wanted to try it out anyway and see whether it works.

Finishing the Word Processing

The last scenario is when a word has 4 or more letters:

Scenario Outline: It should munger a word properly
    When I call my method with <input>
    Then I receive <output>

    Examples:
      | input   | output  |
      | "a"     | "a"     |
      | "an"    | "an"    |
      | "and"   | "and"   |
      | "spice" | "scipe" |

I got a red bar:

With the following Java code I get all green:

At this point my implementation works with a single word, but the exercise requires a sentence as an input. Let’s add a new scenario to the .feature file:

Scenario: It should process a sentence
    When I call my method with "And the spice must flow"
    Then I receive "And the scipe msut folw"

I really like what I’ve seen so far, everything I’m using with cucumber for ruby works with cucumber-jvm too, but a wise man told me once that I should go home with a red test case, because it will show me where I finished my work yesterday. You can find the source for this post under the episode_2 branch of the repository on github. Now, I have a red test case which I’m going to fix it in the next post, where besides the fix I’m going to show how cucumber-jvm works with dependency injection frameworks. Stay tuned!


comments powered by Disqus