Writing Textual Stories

Behaviour-Driven Development encourages you to start defining the stories via scenarios that express the desired behaviour in a textual format, e.g.:

    Given a stock of symbol STK1 and a threshold of 10.0
    When the stock is traded at 5.0
    Then the alert status should be OFF

The textual scenario should use the language of the business domain and shield away as much as possible the details of the technical implementation. Also, it should be given a name that is expressive of the functionality that is being verified, i.e. trader_is_alerted_of_status.story.

The scenario should use a syntax compatible with the Grammar.

A story is a collection of scenarios, each detailing different examples of the behaviour of a given increment of functionality of the system.
    Scenario:  trader is not alerted below threshold

    Given a stock of symbol STK1 and a threshold of 10.0
    When the stock is traded at 5.0
    Then the alert status should be OFF

    Scenario:  trader is alerted above threshold

    Given a stock of symbol STK1 and a threshold of 10.0
    When the stock is traded at 11.0
    Then the alert status should be ON

Mapping Textual Scenario Steps to Java Methods via annotations

JBehave maps textual steps to Java methods via CandidateSteps. The scenario writer need only provide annotated methods that match, by regex patterns, the textual steps. One way this can be done is by extending the default implementation Steps:

public class TraderSteps extends Steps {

    private Stock stock;

    @Given("a stock of symbol $symbol and a threshold of $threshold")
    public void aStock(String symbol, double threshold) {
        stock = new Stock(symbol, threshold);
    }

    @When("the stock is traded at $price")
    public void theStockIsTradedAt(double price) {
        stock.tradeAt(price);
    }

    @Then("the alert status should be $status")
    public void theAlertStatusShouldBe(String status) {
        ensureThat(stock.getStatus().name(), equalTo(status));
    }

}

Equivalently, as composition is often preferrable to inheritance, the scenario writer can use a POJO (i.e. without extending Steps)

public class TraderSteps { // look, Ma, I'm a POJO!!
 
    private Stock stock;

    @Given("a stock of symbol $symbol and a threshold of $threshold")
    public void aStock(String symbol, double threshold) {
        stock = new Stock(symbol, threshold);
    }

    @When("the stock is traded at $price")
    public void theStockIsTradedAt(double price) {
        stock.tradeAt(price);
    }

    @Then("the alert status should be $status")
    public void theAlertStatusShouldBe(String status) {
        ensureThat(stock.getStatus().name(), equalTo(status));
    }

}

If POJOs are used we need to create CandidateSteps via the StepsFactory

    StepsConfiguration configuration = ... // optional configuration
    new StepsFactory(configuration).createCandidateSteps(new TraderSteps()));    

Each step is annotated with one of the step annotations, each holding a regex pattern as value. The pattern is used to match the method in the Steps class with the appropriate parameters. The simplest default behaviour identifies arguments in the candidate step by the words prefixed by the $ character. More advanced parameter injection mechanisms are also supported by JBehave.

JBehave execute all the matched steps in the order in which they are found in the Scenario. It is up to the implementor of the Steps class to provide the logic to tie together the results of the execution of each step. This can be done by keeping state member variables in the Steps class or possibly by using a service API or other dependency.

Mapping Story Files to Java Classes

In JBehave stories can be run in an automated way via a mapping to Java classes, either a one-to-one mapping or a many-to-one mapping.

In the case of one-to-one mapping, the textual story path is resolved from a Java class via the StoryPathResolver. In our example, we need to create a file TraderIsAletedOfStatus.java, which maps to out textual story in same package, and it would extend a base Story class that contains the configuration:

Thus in our case the example Story would look like:

public class TraderStory extends JUnitStory {

    public TraderStory() {
        StoryConfiguration storyConfiguration = new MostUsefulConfiguration();
        storyConfiguration.useStoryPathResolver(new UnderscoredCamelCaseResolver(".story"));
        storyConfiguration.useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()));

        addSteps(new TraderSteps()); // if TraderSteps extends Steps
        addSteps(new StepsFactory().createCandidateSteps(new TraderSteps())); // if TraderSteps is a POJO
    }
}

In the case of many-to-one mapping, the textual story path must be explicitly provided when extending JUnitStories:

public class TraderStories extends JUnitStories {

    public TraderStories() {
        StoryConfiguration storyConfiguration = new MostUsefulConfiguration();
        storyConfiguration.useStoryPathResolver(new UnderscoredCamelCaseResolver(".story"));
        storyConfiguration.useStoryLoader(new LoadFromClasspath(this.getClass().getClassLoader()));

        addSteps(new TraderSteps()); // if TraderSteps extends Steps
        addSteps(new StepsFactory().createCandidateSteps(new TraderSteps())); // if TraderSteps is a POJO
    }

    @Override
    protected List storyPaths() {
        return asList("org/jbehave/examples/trader/stories/trader_is_alerted_of_status.story",
                      "org/jbehave/examples/trader/stories/traders_can_be_subset.story");
    }
}

Here we are configuring our textual story files to end with extension .story, by overriding the default behaviour of UnderscoredCamelCaseResolver which has no extension.

What Next?

The Configuring Stories page will go into more configuration details and Running Stories into the different ways to run stories. Or if you want to learn more about JBehave's step matching mechanism, you'll want to explored the concept of candidate steps in more detail.