JBehave is a framework for Behaviour-Driven Development (BDD). But for more info you should go to the JBehave website

This module is the glue between Unitils and Jbehave.

Dependency

<dependency>
  <groupId>org.unitils.jbehave</groupId>
  <artifactId>unitils-jbehave</artifactId>
  <version>1.x.x</version>
</dependency>

Configuration

JBehave allows to use different steps to tell a story, so we had to change a few modules to make this possible.

If you want to use unitils-mail, than you have to use the following config.

unitils.modules= ...,mail,...
unitils.module.mail.className = org.unitils.jbehave.modules.JBehaveMailModule
unitils.module.mail.runAfter=
unitils.module.mail.enabled=true

If you want to use unitils-selenium, than you have to use the following config.

unitils.modules= ...,webdriver,...
unitils.module.webdriver.className = org.unitils.jbehave.modules.JBehaveWebdriverModule
unitils.module.webdriver.runAfter=
unitils.module.webdriver.enabled=true

How to use it...

You have f.e. a Mail story with a few scenarios where you want to check if a mail has been sent or not.

This is just an example story ...

Scenario:  send correct email
Given send an email to  willemijn.wouters@unitils.be
Then check if the email is sent


Scenario: send correct email with correct header
Given send an email with willemijn.wouters@unitils.be with header     just a testheader
Then check if the email has the correct header

... with a few fictive steps
SimpleMailStep

public class SimpleMailStep {

    @TestSmtpServer
    private SmtpServer smtpServer;

    private SimpleSender mailSender;

    private String address;

    @BeforeScenario
    public void beforeScenarioPhase() {
        String port = (String) Unitils.getInstance().getConfiguration().get(MailModule.SMTP_DEFAULT_PORT);
        mailSender = new SimpleSender(Integer.valueOf(port));
    }

    @Given("send an email to $mailAddress")
    public void soSomeSetUp(@Named("mailAddress") String address) throws MessagingException, IOException {
        mailSender.sendMessage("sender@here.com", "TestHeader", "Test Body", address);
        this.address = address;
}

    @Then("check if the email is sent")
    public void doSomeChecks() throws IOException, MessagingException {
        Assert.assertEquals(1, smtpServer.getReceivedEmailSize());
        List<SmtpMessage> mails = smtpServer.getReceivedEmail();
        SmtpMessageImpl expected = new SmtpMessageImpl("sender@here.com", "TestHeader", "Test Body", address);
        ReflectionAssert.assertLenientEquals(expected, mails.get(0));
    }

SimpleMailStep2

public class SimpleMailStep2 {

    @TestSmtpServer
    private SmtpServer smtpServer;

    private SimpleSender mailSender;

    private String address;

    private String header;

    @BeforeScenario
    public void beforeScenarioPhase() {
        String port = (String) Unitils.getInstance().getConfiguration().get(MailModule.SMTP_DEFAULT_PORT);
        mailSender = new SimpleSender(Integer.valueOf(port));
    }


    @Given("send an email with $emailAddress with header just a $header")
    public void setUp(@Named("emailAddress") String email, @Named("header") String header) throws MessagingException, IOException {
        mailSender.sendMessage("sender@here.com", header, "Test Body", email);
        this.address = email;
        this.header = header;
    }

    @Then("check if the email has the correct header")
    public void doSomeChecks() throws IOException, MessagingException {
        Assert.assertEquals(1, smtpServer.getReceivedEmailSize());
        List<SmtpMessage> mails = smtpServer.getReceivedEmail();
        SmtpMessageImpl expected = new SmtpMessageImpl("sender@here.com", header, "Test Body", address);
        ReflectionAssert.assertLenientEquals(expected, mails.get(0));
    }
}

unitils-jbehave does most of the config for you, but you need to give the story(s)/step(s).

For this test I need my Mail.story, SimpleMailStep and SimpleMailStep2.

public class SimpleMailTest extends UnitilsJunitStories {


    /**

     * @see org.unitils.jbehave.UnitilsJunitStories#getSteps()
     */
    @Override
    public List<Object> getSteps() {
        List<Object> lst = new ArrayList<Object>();
        lst.add(new SimpleMailStep());
        lst.add(new SimpleMailStep2());
        return lst;
    }

    /**

     * @see org.jbehave.core.junit.JUnitStories#storyPaths()
     */
    @Override
    protected List<String> storyPaths() {
        return Arrays.asList("org/unitils/jbehave/stories/Mail.story");
    }

}

Database features

Ofcourse you can use the annotations in the steps but if you reuse youre steps in multiple scenarios than it might be useful to put a meta tag in your story file.

At the moment there are three tags that you can use.

If you want to use this feature than you have to override the configureJBehave() method in your testclass and add the following...

@Override 
public JBehaveConfiguration configureJBehave() {
     return super.configureJBehave()
         .useDatabaseSteps()
         .addSteps(new YourSteps());
}

An example of how your story would look like:

Meta: 
@SqlScript path/to/script.sql 
@Dataset path/to/dataset.xml

Scenario: Title 
Meta: 
@Dataset path/to/scenario/dataset.xml 
@ExpectedDataset path/to/scenario/expected-dataset.xml 
Given ... 
When ... 
Then ...

Warnings:
If you put f.e. an @Dataset meta tag on story and scenario level, than the meta tag on scenario level overrides the meta tag on story level.

The meta tag @SqlScript is always executed before the @DataSet meta tag.

multidatabase (1.0.3)
If you use multiple databases and you want to define the database in your dataset/script, than code as followed:

dataset

<dataset>
    <database name="database1"/>
    <testperson id="3" name="Maurits"/>
    <testperson id="4" name="Frans"/>
</dataset>

script

--Database name=database1
insert into testperson values('5', 'Daniella');

There is also an extra meta tag for Testlink, for if you want to use our unitils-testlink module in combination with JBehave.

If you want to use this feature than you have to override the configureJBehave() method in your testclass and add the following...

@Override 
public JBehaveConfiguration configureJBehave() {
     return super.configureJBehave()
        .addSteps(new YourSteps())
                         .addFormat(org.unitils.jbehave.core.reporters.UnitilsFormats.TESTLINK); 
}

An example of how your story would look like:

Scenario: Title 
Meta: 
@TestLinkId PRJ-4 
Given ... 
When ... 
Then ...

You should add the correct properties in the unitils.properties for unitils-testlink to make it completely work.

Screenshots when a step fails

If you want that unitils-selenium automatically creates a screenshot when a step fails, than you can add the SeleniumScreenshotReporter to your configuration.

@Override 
public JBehaveConfiguration configureJBehave() {
     SeleniumSteps yourSeleniumSteps = new YourSeleniumSteps();
     SeleniumScreenshotReporter seleniumScreenshotReporter = new    SeleniumScreenshotReporter("screenshots", yourSeleniumSteps);
     return super.configureJBehave()
         .addSteps(yourSeleniumSteps)
         .addStoryReporter(seleniumScreenshotReporter)
         .addStepsWithSeleniumReporter(new YourSeleniumSteps(), "screenshots"); 
}

YourSeleniumSteps implements the org.unitils.jbehave.core.reporters.SeleniumSteps interface and contains a ScreenshotTakingwebdriver that can be used in your steps. In this case the screenshots are created in the "screenshots" directory in your target directory.

Rapportation

Jbehave creates rapports in the target/jbehave folder.
This is an overview of all the succeeded and failed tests.

If there is something wrong, than you can click on the story to check which scenarios are passing and which are failing and why it is failing.

Useful JBehave features

1. Specifying story files
By default any .story file within any package is included in the test. This can be changed to a specific story file or/and story package.

@Override 
public JBehaveConfiguration configureJBehave() {
     return super.configureJBehave()
         .storyPackage("name/project/jbehave")
         .storyFile("TestCase.story");
}

With this code example, only the TestCase.story file within package name/project/jbehave is run. It is allowed to use wildcards in the story file or story package. For example to run any story file within the package you could modify the configuration to:

@Override 
public JBehaveConfiguration configureJBehave() {
     return super.configureJBehave()
         .storyPackage("name/project/jbehave")
         .storyFile("*.story"); 
}

2. Filtering scenarios
During the development phase of the jbehave it can be convenient to just one specific scenario in a story file containing several scenarios. Or when using different maven profiles, another set of scenarios need to be run. To achieve these goals, we can make use of the JBehave Meta Filters. There are 3 ways we can specify meta filters:

  1. org.unitils.jbehave.meta_filters property within unitils.properties
  2. @MetaFilters annotation on the StoryTest class this overwrites the meta filters specified in unitils.properties
  3. add them within configureJBehave() method
    @Override    
    public JBehaveConfiguration configureJBehave() {
            return super.configureJBehave()
                .addSteps(new YourSteps())
                .addMetaFilter("-skip")
                .addMetaFilter("+level one");    
    }

3. Keyword aliases
It is possible to create a keyword alias. For example when you want 'With' to be the alias of the keyword 'And' you can use following configuration:

@Override 
public JBehaveConfiguration configureJBehave() {
     return super.configureJBehave()
         .addSteps(new YourSteps())
         .addKeywordAlias(Keywords.AND, "With"); 
}

and add this in youre story file:

Scenario: Title 
Given a setup 
With something extra 
When something happens 
Then something is expected

4. Variables within the story file
It is possible to make use of variables within a story file without any extra configuration. This is enabled by default. The values of the variables are specified in the jbehave.properties.

dataset_dir  = path/to/datasets

This value will be inserted in all story files that contain ${dataset_dir}.

Scenario: Title 
Meta: 
@Dataset ${dataset_dir}/dataset.xml 
Given ... 
When ... 
Then ...
 

Last edit: Willemijn Wouters 2015-05-19