Google

Nov 6, 2013

jBehave with ExamplesTable

This extends the previous tutorials
Step 1: The story file  which supplies a list of transactions as an input (i.e. via Given) and validates against a list of transactions that are supplied as shown below in Then.

Narrative: As a txns processor verify the number of txns returned

Scenario: verify transactions 

Given a list of transactions
txnCode|txndate|txnAmount
A123|10/07/2005|500.00
A124|10/25/2005|500.00
A125|10/28/2005|500.00
A126|10/29/2005|500.00
A127|10/15/2005|500.00
A128|10/12/2005|500.00

When processTxn method is fired with accountCd = 1234

Then I expect to receive 5 transactions
Then I expect transactions to have
TransactionType|TransactionSubType|OtherIncomeExpType
Income|InterestRevenue|NotApplicable
Income|InterestRevenue|NotApplicable
Expense|OtherIncome|NotApplicable
EXPENSE|InterestRevenue|NotApplicable
INCOME|NotApplicable|NotApplicable


Step 2: Map the story to step class TransactionProcessorStep as shown below. Pay attention to the ExamplesTable class provided by jBehave library.

//...

import org.drools.runtime.StatelessKnowledgeSession;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.model.ExamplesTable;
import org.joda.time.DateTime;
import org.mockito.Mockito;
import org.springframework.stereotype.Component;

@Component
public class TransactionProcessorStep
{

    //...
 //input
 List<Transaction> transactionsList = Collections.EMPTY_LIST;
 //output
 List<Transaction> result;

    @Given("a list of transactions $cashTransactionsTable")
    public void cashTxns(ExamplesTable cashTransactionsTable)
    {
        this.transactionsList = toTransactionsList(cashTransactionsTable);
    }
 
 @When("processTxn method is fired with accountCd = $accountCode")
    public void processTransaction(String accountCode)
    {
 
        try
        {
            //logic to process actual method and get the result
   result = ...
            
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        
    }

    @Then("I expect to receive $count transactions")
    public void verify(int count)
    {
        try
        {
            MatcherAssert.assertThat(count, Matchers.equalTo(result.size()));
            
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    } 
 
 @Then("I expect transactions to have $transactionsTable")
    public void verify2(ExamplesTable transactionsTable)
    {
        try
        {
            
            int i = 0;
            for (Map<String, String> row : transactionsTable.getRows())
            {
                
                Transaction transaction = result.get(i);
                
                String txnType = row.get("TransactionType");
                MatcherAssert.assertThat(transaction.getTransactionType().name(),
                        Matchers.equalTo(txnType));
                
     String analysisCode = row.get("TransactionSubType");
                MatcherAssert.assertThat(transaction.getAnalysisCode(),
                        Matchers.equalTo(analysisCode));
    
                String otherIncomeExpType = row.get("OtherIncomeExpType");
                MatcherAssert.assertThat(transaction.getOtherIncomeAndExpensesSubType().name(),
                        Matchers.equalTo(otherIncomeExpType));
                 
                ++i;
                
            }
            
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
 
 private List<Transaction> toTransactionsList(ExamplesTable table)
    {
        List<Transaction> ctList = new ArrayList<Transaction>();
        for (Map<String, String> row : table.getRows())
        {
            
            //txnCode|txndate|txnAmount
            String txnCode = row.get("analysisCd");
            String txndate = row.get("txndate");
            String txnAmount = row.get("txnAmount");
   
            Transaction ct = new Transaction();
            ct.setAnalysisCd(txnCode);
            
            SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
            try
            {
                ct.setTxnDt(sdf.parse(txndate));
            }
            catch (ParseException e)
            {
                throw new RuntimeException(e);
            }
            ct.setTxnAmount(new BigDecimal(txnAmount));
            
            ctList.add(ct);
        }
        return ctList;
    }
 

Labels: ,

Oct 22, 2013

jBehave Tutorial

This extends the previous tutorials
To appreciate jBehave, let's look at a better example here. This example is about a science formula

  Speed = distance / Time.

So,

  • given  distance and time, calculate speed
  • given speed and time, calculate distance
  • given speed and distance, calculate time.

Step 1: The story file in plain english. speed.story under src/main/resources/jBehave folder.


Narrative: As a student, I want to practice speed formula where speed = distance / time

scenario: calculate speed from distance and time

Given distance = 10.0
Given time = 2.5
When calculate speed
Then verify speed = 4.0


scenario: calculate distance from speed and time

Given speed = 4.0
Given time = 2.5
When calculate distance
Then verify distance = 10.00

scenario: calculate time from speed and distance

Given speed = 4.0
Given distance = 10.0
When calculate time
Then verify time = 2.50


Step 2: Mapping the story to Java step class. The steps are fine grained so that they can be mixed and matched depending on th scenario.

package com.mycompany.jbehave2;

import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Named;
import org.jbehave.core.annotations.Then;
import org.jbehave.core.annotations.When;
import org.jbehave.core.steps.Steps;
import org.springframework.util.Assert;

public class SpeedVelocityFormulaSteps extends Steps
{
    
    SpeedVelocityFormaula svf = new SpeedVelocityFormulaImpl();
    
    private double speed;
    private double distance;
    private double time;
    
    private double resultSpeed;
    private double resultDistance;
    private double resultTime;
    
    @Given("speed = $speed")
    public void speedInput(@Named("speed") double speed)
    {
        this.speed = speed;
    }
    
    @Given("distance = $distance")
    public void distanceInput(@Named("distance") double distance)
    {
        this.distance = distance;
    }
    
    @Given("time = $time")
    public void timeInput(@Named("time") double time)
    {
        this.time = time;
    }
    
    @When("calculate speed")
    public void calcSpeed()
    {
        Assert.notNull(distance);
        Assert.notNull(time);
        
        resultSpeed = svf.calculateSpeed(this.distance, this.time);
    }
    
    @When("calculate distance")
    public void calcDistance()
    {
        Assert.notNull(speed);
        Assert.notNull(time);
        
        resultDistance = svf.calculateDistance(this.speed, this.time);
    }
    
    @When("calculate time")
    public void calcTime()
    {
        Assert.notNull(speed);
        Assert.notNull(distance);
        
        resultTime = svf.calculateTime(this.speed, this.distance);
    }
    
    @Then("verify speed = $speedSupplied")
    public void verifySpeed(double speedSupplied)
    {
        Assert.notNull(distance);
        Assert.notNull(time);
        
        junit.framework.Assert.assertEquals(speedSupplied, resultSpeed);
    }
    
    @Then("verify distance = $distanceSupplied")
    public void verifyDistance(double distanceSupplied)
    {
        Assert.notNull(this.speed);
        Assert.notNull(time);
        
        junit.framework.Assert.assertEquals(distanceSupplied, resultDistance);
    }
    
    @Then("verify time = $timeSupplied")
    public void verifyTime(double timeSupplied)
    {
        Assert.notNull(speed);
        Assert.notNull(distance);
        
        junit.framework.Assert.assertEquals(timeSupplied, resultTime);
    }
    
}

Step 3: The actual interface that performs the calculations.

package com.mycompany.jbehave2;

public interface SpeedVelocityFormaula
{
    abstract double calculateSpeed(double distance, double time);
    
    abstract double calculateDistance(double speed, double time);
    
    abstract double calculateTime(double speed, double distance);
}

Step 4: The implementation. This is the class under test.


package com.mycompany.jbehave2;

public class SpeedVelocityFormulaImpl implements SpeedVelocityFormaula
{  
    public double calculateSpeed(double distance, double time)
    {
        return distance / time; // s = d/t
    }
    
    public double calculateDistance(double speed, double time)
    {
        return speed * time; //speed * time
    }
    
    public double calculateTime(double speed, double distance)
    {
        return distance / speed;  //distance/speed
    }
    
}


Step 5: Finally the jUnit test class

package com.mycompany.jbehave2;

import de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner;

import java.util.Arrays;
import java.util.List;

import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.junit.runner.RunWith;

@RunWith(JUnitReportingRunner.class)
public class JBehaveUnitTest extends JUnitStories
{
    
    public JBehaveUnitTest()
    {
        super();
    }
    
    public InjectableStepsFactory stepsFactory()
    {
        return new InstanceStepsFactory(configuration(), new SpeedVelocityFormulaSteps());
    }
    
    @Override
    protected List<String> storyPaths()
    {
        return Arrays.asList("jbehave/speed.story");
    }
    
}

Step 6: Run th unit test.



Labels: ,

Oct 17, 2013

jBehave and jUnit for BDD

This extends the jBehave -- Behavioural Driven Development (BDD) to use the same example with jUnit testing.

Step 1:  Make sure that you have both jUnir and jBehave in your pom.xml file.

 
<dependency>
 <groupId>org.jbehave</groupId>
 <artifactId>jbehave-core</artifactId>
 <version>3.8</version>
</dependency>

<dependency>
 <groupId>junit</groupId>
 <artifactId>junit</artifactId>
 <version>4.10</version>
 <scope>test</scope>
</dependency>


Step 2: Refer to Behavioural Driven Development (BDD) for all the jBehave implementation.

Step 3: The jUnit class with jBehave. It extends the  JUnitStories class.

 
package com.mycompany.jbehave;

import java.util.Arrays;
import java.util.List;

import org.jbehave.core.junit.JUnitStories;

public class JBehaveUnitTest extends JUnitStories
{
    
    public JBehaveUnitTest()
    {
        super();
        this.configuredEmbedder().candidateSteps().add(new MathSteps());
    }
    
    @Override
    protected List<string>> storyPaths()
    {
        return Arrays.asList("jbehave/math.story");
    }
    
}


Step 4: You can run the above class as jUnit, but won't give you enough info and not very user friendly. This is where the jBehave jUnit extansion becomes very handy. This can be added to your maven pom.xml file as shown below.

 
<dependency>
 <groupId>de.codecentric</groupId>
 <artifactId>jbehave-junit-runner</artifactId>
 <version>1.0.2</version>
</dependency>


Step 5: Now, the revised jUnit test class from Step 3.

 
package com.mycompany.jbehave;

import de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner;

import java.util.Arrays;
import java.util.List;

import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.junit.runner.RunWith;

@RunWith(JUnitReportingRunner.class)
public class JBehaveUnitTest extends JUnitStories
{
    
    public JBehaveUnitTest()
    {
        super();
    }
    
    public InjectableStepsFactory stepsFactory()
    {
        return new InstanceStepsFactory(configuration(), new MathSteps());
    }
    
    @Override
    protected List<String> storyPaths()
    {
        return Arrays.asList("jbehave/math.story");
    }
    
}

Step 6: Running it as jUnit within eclipse looks like.



Isn't it great? Communication is the key to jBehave as the scenarios are written in simple English that business and developers can work together.

Labels: ,

Oct 16, 2013

jBehave -- Behavioural Driven Development (BDD)

Q. What is BDD?
A. BDD is principally an idea about how software development should be managed by both business interests and technical insight. Test-driven development focuses on the developer’s opinion on how parts of the software should work. Behavior-driven development focuses on the users’ opinion on how they want your application to behave. So, when you start writing a test, you need to think about the stories, and each story should cover three things:

  • Given :  an input value of 2
  • When : you multiply  the input with 3
  • Then : result should be 6

Even you write unit tests as part of TDD (Test Driven Development) or without TDD , you need to think about Given ... When ... Then ...

Here is a simple  example using the jBehave framework in Java. jBehave tutorial.

Step 1:  Maven pom.xml file on jBehave dependency


 
<dependency>
 <groupId>org.jbehave</groupId>
 <artifactId>jbehave-core</artifactId>
 <version>3.8</version>
</dependency>


Step 2: Define the story in plain English that business users and testers can understand using Given... When Then... style. The math.story file under  src/main/resources/jbehave folder


 
Scenario: 2 squared

Given a variable input with value 2
When I multiply input by 2 
Then result should equal 4

Scenario: 3 squared

Given a variable input with value 3
When I multiply input by 3 
Then result should equal 9



Step 3: Map the above scenarios based stories to Java equivalent.

 
package com.mycompany.jbehave;

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

public class MathSteps extends Steps
{
    private int input;
    private int result;
    
    @Given("a variable input with value $value")
    public void givenInputValue(@Named("value") int value)
    {
        input = value;
    }
    
    @When("I multiply input by $value")
    public void whenImultiplyInputBy(@Named("value") int value)
    {
        result = input * value;
    }
    
    @Then("result should equal $value")
    public void thenInputshouldBe(@Named("value") int value)
    {
        if (value != result)
            throw new RuntimeException("result is " + result + ", but should be " + value);
    }
}


Step 4: Write a main class to excute the scenarios.

 
package com.mycompany.jbehave;

import java.util.Arrays;
import java.util.List;

import org.jbehave.core.embedder.Embedder;

public class JBehaveTest
{
    private static Embedder embedder = new Embedder();
    private static List<String> storyPaths = Arrays
            .asList("jbehave/math.story");
    
    public static void main(String[] args)
    {
        embedder.candidateSteps().add(new MathSteps());
        try
        {
            embedder.runStoriesAsPaths(storyPaths);
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
        
    }
}


Step 5: Run it to get output:

 
Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeoutInSecs=300,threads=1]
Running story jbehave/math.story
Generating reports view to 'C:\projects\my-app-parent\my-app\target\jbehave' using formats '[]' and view properties '{defaultFormats=stats, decorateNonHtml=true, viewDirectory=view, decorated=ftl/jbehave-report-decorated.ftl, reports=ftl/jbehave-reports-with-totals.ftl, maps=ftl/jbehave-maps.ftl, navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl}'
Reports view generated with 0 stories (of which 0 pending) containing 0 scenarios (of which 0 pending)


Now, modify the math.story file to have an error for the second  scenario as shown below. 3 * 3 is not 10.

 
Scenario: 2 squared

Given a variable input with value 2
When I multiply input by 2 
Then result should equal 4

Scenario: 3 squared

Given a variable input with value 3
When I multiply input by 3 
Then result should equal 10


Run it again with the change, and you will get an error as shown below for the second scenario.

 
Processing system properties {}
Using controls EmbedderControls[batch=false,skip=false,generateViewAfterStories=true,ignoreFailureInStories=false,ignoreFailureInView=false,verboseFailures=false,verboseFiltering=false,storyTimeoutInSecs=300,threads=1]
Running story jbehave/math.story
Generating reports view to 'C:\projects\my-app-parent\my-app\target\jbehave' using formats '[]' and view properties '{defaultFormats=stats, decorateNonHtml=true, viewDirectory=view, decorated=ftl/jbehave-report-decorated.ftl, reports=ftl/jbehave-reports-with-totals.ftl, maps=ftl/jbehave-maps.ftl, navigator=ftl/jbehave-navigator.ftl, views=ftl/jbehave-views.ftl, nonDecorated=ftl/jbehave-report-non-decorated.ftl}'
Reports view generated with 0 stories (of which 0 pending) containing 0 scenarios (of which 0 pending)
org.jbehave.core.embedder.Embedder$RunningStoriesFailed: Failures in running stories: 
jbehave/math.story: org.jbehave.core.embedder.StoryManager$StoryExecutionFailed: jbehave/math.story
 at org.jbehave.core.embedder.Embedder$ThrowingRunningStoriesFailed.handleFailures(Embedder.java:495)
 at org.jbehave.core.embedder.Embedder.handleFailures(Embedder.java:224)
 at org.jbehave.core.embedder.Embedder.runStoriesAsPaths(Embedder.java:205)
 at com.mycompany.jbehave.JBehaveTest.main(JBehaveTest.java:19)



Next post will use junit for testing the scenarios.

Labels: , ,