Sunday, June 2, 2013

jBehave Tutorial: BDD Framework for Verifying User Stories

Overview:
This article looks at jBehave, a popular BDD framework for Java.

There are many ways in which you could configure jBehave. Here, we look at the minimal set of configuration required to get the framework integrated with your BDD environment.

Sample code can be found here.

Project Structure:

org.fazlan.jbehave.exmaple
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── org
    │           └── fazlan
    │               └── jbehave
    │                   └── exmaple
    │                       └── Calculator.java
    └── test
        ├── java
        │   └── org
        │       └── fazlan
        │           └── jbehave
        │               └── exmaple
        │                   ├── steps
        │                   │   └── AddTwoNumbersSteps.java
        │                   └── stories
        │                       └── CalculatorStories.java
        └── resources
            └── org
                └── fazlan
                    └── jbehave
                        └── exmaple
                            └── stories
                                └── calculator_stories.story

Step 1: Creating the Project

 mvn archetype:generate -DartifactId=org.fazlan.jbehave.exmaple -DgroupId=org.fazlan -Dversion=1.0-SNAPSHOT -DinteractiveMode=false  

Step 2: Updating the Maven dependencies
Update (2013-10-7) : Updated to include maven-jbehave-plugin.

NOTE: This assumes that you run only acceptance tests at maven integration-test phase, any unit tests will be skipped as per the pom.xml configuration.

 <?xml version="1.0" encoding="UTF-8"?>  
 <project xmlns="http://maven.apache.org/POM/4.0.0"  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan</groupId>  
   <artifactId>org.fazlan.jbehave.exmaple</artifactId>  
   <version>1.0-SNAPSHOT</version>  
   <properties>  
     <jbehave.version>3.8</jbehave.version>  
     <junit.version>4.11</junit.version>  
     <failsafe.and.surefire.version>2.16</failsafe.and.surefire.version>  
   </properties>  
   <dependencies>  
     <dependency>  
       <groupId>junit</groupId>  
       <artifactId>junit</artifactId>  
       <version>${junit.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.jbehave</groupId>  
       <artifactId>jbehave-core</artifactId>  
       <version>${jbehave.version}</version>  
     </dependency>  
   </dependencies>  
   <build>  
     <plugins>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-surefire-plugin</artifactId>  
         <version>${failsafe.and.surefire.version}</version>  
         <configuration>  
           <!-- Disable unit tests -->  
           <skip>true</skip>  
         </configuration>  
       </plugin>  
       <plugin>  
         <groupId>org.apache.maven.plugins</groupId>  
         <artifactId>maven-failsafe-plugin</artifactId>  
         <version>${failsafe.and.surefire.version}</version>  
         <executions>  
           <execution>  
             <id>integration-test</id>  
             <goals>  
               <goal>integration-test</goal>  
               <goal>verify</goal>  
             </goals>  
           </execution>  
         </executions>  
         <configuration>  
           <includes>  
             <include>**/*Stories.java</include>  
           </includes>  
         </configuration>  
       </plugin>  
       <plugin>  
         <groupId>org.jbehave</groupId>  
         <artifactId>jbehave-maven-plugin</artifactId>  
         <version>${jbehave.version}</version>  
         <executions>  
           <execution>  
             <id>run-stories-as-embeddables</id>  
             <phase>integration-test</phase>  
             <configuration>  
               <testSourceDirectory>${basedir}/src/test/java</testSourceDirectory>  
               <includes>  
                 <include>**/*Stories.java</include>  
               </includes>  
               <ignoreFailureInStories>false</ignoreFailureInStories>  
               <ignoreFailureInView>false</ignoreFailureInView>  
             </configuration>  
             <goals>  
               <goal>run-stories-as-embeddables</goal>  
             </goals>  
           </execution>  
         </executions>  
       </plugin>  
     </plugins>  
   </build>  
 </project>  

Step 3: Defining the User story

A User story describes the behaviour of a piece of software and the expected outcome (acceptance criteria) for a given set of preconditions.

The following contains a simple user story for adding two numbers, which is defined in org/fazlan/jbehave/exmaple/stories/calculator_stories.story

 Narrative:  
 In order to quickly find out the sum of two numbers  
 As a user  
 I want to use a calculator to add two numbers
  
 Scenario: Add two valid numbers
  
 Given a calculator  
 When I add <number1> and <number2>  
 Then the outcome should <result>
  
 Examples:  
 |number1|number2|result|  
 |10|10|20|  

Step 4: Writing the test to verify the story

The following is the JUnit test for verifying and testing our add two numbers story.

 package org.fazlan.jbehave.exmaple.stories; 
 
 import org.fazlan.jbehave.exmaple.steps.AddTwoNumbersSteps; 
 
 import org.jbehave.core.configuration.Configuration;  
 import org.jbehave.core.junit.JUnitStory;  
 import org.jbehave.core.reporters.StoryReporterBuilder;  
 import org.jbehave.core.steps.InjectableStepsFactory;  
 import org.jbehave.core.steps.InstanceStepsFactory;  

 import static org.jbehave.core.reporters.Format.CONSOLE;  
 import static org.jbehave.core.reporters.Format.TXT;
  
 public class CalculatorStories extends JUnitStory {
  
   @Override  
   public Configuration configuration() {  
     return super.configuration()  
         .useStoryReporterBuilder(  
             new StoryReporterBuilder()  
                 .withDefaultFormats()  
                 .withFormats(CONSOLE, TXT));  
   }  

   // Here we specify the steps classes  
   @Override  
   public InjectableStepsFactory stepsFactory() {  
     return new InstanceStepsFactory(configuration(), new AddTwoNumbersSteps());  
   }  
 }  


IMPORTANT NOTE: By default, jBehave framework will look for *.story files defined in the same package as the corresponding *Stories.java file.

e.i: org.fazlan.jbehave.exmaple.stories.CalculatorStories.java will look for a *.story file called org/fazlan/jbehave/exmaple/stories/calculator_stories.story

Assume, you have defined your story in a file called 'CalculatorStory.story' then you can change the default behaviour of discovering stories by overriding the List<String> storyPaths() method in the following way,

 package org.fazlan.jbehave.exmaple.stories;  

 import org.fazlan.jbehave.exmaple.steps.AddTwoNumbersSteps;  

 import org.jbehave.core.configuration.Configuration;  
 import org.jbehave.core.junit.JUnitStory;  
 import org.jbehave.core.reporters.StoryReporterBuilder;  
 import org.jbehave.core.steps.InjectableStepsFactory;  
 import org.jbehave.core.steps.InstanceStepsFactory;  

 import static org.jbehave.core.reporters.Format.CONSOLE;  
 import static org.jbehave.core.reporters.Format.TXT;  

 public class CalculatorStories extends JUnitStory {  

   @Override  
   public Configuration configuration() {  
     return super.configuration()  
         .useStoryReporterBuilder(  
             new StoryReporterBuilder()  
                 .withDefaultFormats()  
                 .withFormats(CONSOLE, TXT));  
   }  

   // Here we specify the steps classes  
   @Override  
   public InjectableStepsFactory stepsFactory() {  
     return new InstanceStepsFactory(configuration(), new AddTwoNumbersSteps());  
   }  

   @Override  
   protected List<String> storyPaths() {  
     return new StoryFinder().findPaths(org.jbehave.core.io.CodeLocations.codeLocationFromPath("src/test/resources"), "**/CalculatorStory.story", "");  
   }  
 }  

Step 5: Writing the jBehave Step that verifies your story
There are multiple ways to write jBehave Steps, but in this article we focus on keeping this simple and write our Steps as POJOs.

 package org.fazlan.jbehave.exmaple.steps;  

 import org.fazlan.jbehave.exmaple.Calculator;  

 import org.jbehave.core.annotations.Given;  
 import org.jbehave.core.annotations.Named;  
 import org.jbehave.core.annotations.Then;  
 import org.jbehave.core.annotations.When;  

 public class AddTwoNumbersSteps {  

   private Calculator calculator;  

   @Given("a calculator")  
   public void givenACalculator() {  
     calculator = new Calculator();  
   }  

   @When("I add <number1> and <number2>")  
   public void whenIAddNumber1AndNumber2(@Named("number1")int number1, @Named("number2")int number2) {  
     calculator.add(number1, number2);  
   }  

   @Then("the outcome should <result>")  
   public void thenTheOutcomeShould(@Named("result")int result) {  
     assert calculator.getResult() == result;  
   }  
 }  

Step 6: Implementing the Calculator.java to meet our User story acceptance criteria
Following is a simple implementation of a Calculator that has a method called 'add' to add two numbers.

 package org.fazlan.jbehave.exmaple;
  
 public class Calculator {  

   private int result;  

   public void add(int number1, int number2) {  
     result = number1 + number2;  
   }  

   public int getResult() {  
     return result;  
   }  
 }  


Step 7: Running your tests
You can run the acceptance tests issuing the following Maven command.

mvn clean integration-test

Summary:
This was a brief article on how to integrate jBehave with Maven for BDD with your application.

Sample code can be found here.