Configuration

JBehave was designed to be highly embeddable in its configuration and execution. At its heart, we have the Embeddable interface, which can specify the Embedder to use for the running of the stories.

The Embedder separates the concerns of the configuration of the story execution from the matching of the textual story steps to Java methods:

  • Configuration instances are responsible for the configuration of story execution.
  • CandidateSteps instances are used to find the Java methods that match the textual steps in the stories.

The Embedder needs both the Configuration and the list of CandidateSteps. Although it has sensible defaults for the configuration (such as MostUsefulConfiguration) we need, for any significant story running, to configure at least one steps instance.

JBehave allows multiple paths to the configuration of the Embedder, so that users can choose the one that best fits their need.

Using AnnotatedEmbedder

JBehave supports the ability to specify configuration elements and steps instance via annotations. An example of annotated configuration of the Embedded via annotations is:

@RunWith(AnnotatedEmbedderRunner.class)
@Configure(storyControls = MyStoryControls.class, storyLoader = MyStoryLoader.class, storyReporterBuilder = MyReportBuilder.class,
        parameterConverters = { MyDateConverter.class })
@UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = true, ignoreFailureInView = true, verboseFailures = true,
                storyTimeoutInSecs = 100, threads = 2, metaFilters = "-skip")
@UsingSteps(instances = { TraderSteps.class, BeforeAfterSteps.class, AndSteps.class, CalendarSteps.class,
        PriorityMatchingSteps.class, SandpitSteps.class, SearchSteps.class })
public class AnnotatedTraderEmbedder extends InjectableEmbedder {
 
    @Test
    public void run() {
        List<String> storyPaths = new StoryFinder().findPaths(codeLocationFromClass(this.getClass()), "**/*.story", "");
        injectedEmbedder().runStoriesAsPaths(storyPaths);
    }
 
    public static class MyStoryControls extends StoryControls {
        public MyStoryControls() {
            doDryRun(false);
            doSkipScenariosAfterFailure(false);
        }
    }
 
    public static class MyStoryLoader extends LoadFromClasspath {
        public MyStoryLoader() {
            super(AnnotatedTraderEmbedder.class.getClassLoader());
        }
    }
 
    public static class MyReportBuilder extends StoryReporterBuilder {
        public MyReportBuilder() {
            this.withFormats(CONSOLE, TXT, HTML, XML).withDefaultFormats();
        }
    }
 
    public static class MyRegexPrefixCapturingPatternParser extends RegexPrefixCapturingPatternParser {
        public MyRegexPrefixCapturingPatternParser() {
            super("%");
        }
    }
 
    public static class MyDateConverter extends DateConverter {
        public MyDateConverter() {
            super(new SimpleDateFormat("yyyy-MM-dd"));
        }
    }
 
}

Here we are actually using a JUnit Runner, AnnotatedEmbedderRunner, to bootstrap the configuration process. The AnnotatedEmbedderRunner is a very thin wrapper around the AnnotationBuilder, building the Embedder and injecting in the test class, which extends InjectableEmbedder.

Note that JBehave follows a configuration-by-convention approach, by which a default value of the configuration element is always provided but can be overridden, when needed.

Using AnnotationBuilder

If we wanted to use the AnnotationBuilder directly, the above example would become:

@Configure(storyLoader = MyStoryLoader.class, storyReporterBuilder = MyReportBuilder.class,
        parameterConverters = { MyDateConverter.class })
@UsingEmbedder(embedder = Embedder.class, generateViewAfterStories = true, ignoreFailureInStories = true, ignoreFailureInView = true, verboseFailures = true,
                storyTimeoutInSecs = 100, threads = 2, metaFilters = "-skip")
@UsingSteps(instances = { TraderSteps.class, BeforeAfterSteps.class, AndSteps.class, CalendarSteps.class,
        PriorityMatchingSteps.class, SandpitSteps.class })
public class TraderAnnotatedEmbedder {
 
    private Embedder embedder;
     
    public TraderAnnotatedEmbedder(){
        embedder = new AnnotationBuilder(this).buildEmbedder();
    }
     
    @Test
    public void run() {
        embedder.runStoriesAsPaths(new StoryFinder().findPaths(codeLocationFromClass(this.getClass()).getFile(),
                asList("**/*.story"), asList("")));
    }
 
    public static class MyReportBuilder extends StoryReporterBuilder {
        public MyReportBuilder() {
            this.withFormats(CONSOLE, TXT, HTML, XML).withDefaultFormats();
        }
    }
 
    public static class MyStoryLoader extends LoadFromClasspath {
        public MyStoryLoader() {
            super(TraderAnnotatedEmbedder.class.getClassLoader());
        }
    }
 
    public static class MyRegexPrefixCapturingPatternParser extends RegexPrefixCapturingPatternParser {
        public MyRegexPrefixCapturingPatternParser() {
            super("%");
        }
    }
 
    public static class MyDateConverter extends DateConverter {
        public MyDateConverter() {
            super(new SimpleDateFormat("yyyy-MM-dd"));
        }
    }
 
}
The AnnotationBuilder requires that configuration elements and the steps instances provide a default constructor. This limitation is overcome by using dependency injection containers. Note that the use of dependency injection annotations overrides the one specified by the @Configure and @UsingSteps.

Using ConfigurableEmbedder

A ConfigurableEmbedder is an abstract implementation of Embeddable, which allows subclasses to specify the Configuration and CandidateSteps directly. A notable subclass of ConfigurableEmbedder is JUnitStory, which allows each subclass to be run as a separate individual story.

Even if each JUnitStory Java class can be configured independently, it is good practice to collect the configuration that applies to all stories in an abstract (i.e. not runnable) base class:

@RunWith(JUnitReportingRunner.class)
public abstract class TraderStory extends JUnitStory {
 
    public TraderStory() {
        configuredEmbedder().embedderControls().doGenerateViewAfterStories(true).doIgnoreFailureInStories(true)
                .doIgnoreFailureInView(true).useThreads(2).useStoryTimeoutInSecs(60);
        // Uncomment to set meta filter, which can also be set via Maven
        // configuredEmbedder().useMetaFilters(Arrays.asList("+theme parametrisation"));
    }
 
    @Override
    public Configuration configuration() {
        Class<? extends Embeddable> embeddableClass = this.getClass();
        Properties viewResources = new Properties();
        viewResources.put("decorateNonHtml", "true");
        // Start from default ParameterConverters instance
        ParameterConverters parameterConverters = new ParameterConverters();
        // factory to allow parameter conversion and loading from external
        // resources (used by StoryParser too)
        ExamplesTableFactory examplesTableFactory = new ExamplesTableFactory(new LocalizedKeywords(),
                new LoadFromClasspath(embeddableClass), parameterConverters);
        // add custom converters
        parameterConverters.addConverters(new DateConverter(new SimpleDateFormat("yyyy-MM-dd")),
                new ExamplesTableConverter(examplesTableFactory));
 
        return new MostUsefulConfiguration()
                .useStoryControls(new StoryControls().doDryRun(false).doSkipScenariosAfterFailure(false))
                .useStoryLoader(new LoadFromClasspath(embeddableClass))
                .useStoryParser(new RegexStoryParser(examplesTableFactory))
                .useStoryPathResolver(new UnderscoredCamelCaseResolver())
                .useStoryReporterBuilder(
                        new StoryReporterBuilder()
                                .withCodeLocation(CodeLocations.codeLocationFromClass(embeddableClass))
                                .withDefaultFormats().withPathResolver(new ResolveToPackagedName())
                                .withViewResources(viewResources).withFormats(CONSOLE, TXT, HTML, XML)
                                .withFailureTrace(true).withFailureTraceCompression(true))
                .useParameterConverters(parameterConverters));
    }
 
    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(configuration(), new TraderSteps(new TradingService()), new AndSteps(), new MetaParametrisationSteps(),
                new CalendarSteps(), new PriorityMatchingSteps(), new PendingSteps(), new ParametrisedSteps(), new SandpitSteps(),
                new SearchSteps(), new BeforeAfterSteps(), new CompositeSteps(), new CompositeNestedSteps(), new NamedParametersSteps());
    }
 

Once we have a configured instance of ConfigurableEmbedder, all we need to do is to extend it providing the name of the executable Story class that maps to the textual story file. For example, to map to trader_is_alerted_of_status.story using the resolver defined above:

public class TraderIsAlertedOfStatus extends TraderStory {
 
}

To run multiple stories, specified as story paths, one can use another subclass of ConfigurableEmbdder, JUnitStories:

@RunWith(JUnitReportingRunner.class)
public class TraderStories extends JUnitStories {
 
    public TraderStories() {
        // configure as TraderStory except for
        Configuration configuration = new MostUsefulConfiguration()
                .useStoryLoader(new LoadFromURL())
    }
 
    @Override
    protected List<String> storyPaths() {
        String codeLocation = codeLocationFromClass(this.getClass()).getFile();
        return new StoryFinder().findPaths(codeLocation, asList("**/trader*.story"),
                    asList(""), "file:"+codeLocation);
    }
Note that in this second example, we are using story paths as URLs, and correspondingly we configure the use of LoadFromURL.

Scanning for steps

JBehave also supports the ability to scan packages for the Steps classes. If using the annotated approach:

@UsingSteps(packages = { "org.jbehave.examples.core.steps", "my.other.steps" }, matchingNames = ".*Steps", notMatchingNames = ".*SkipSteps" )

If using the programmatic approach:

public InjectableStepsFactory stepsFactory() {
    return new ScanningStepsFactory(configuration(), "org.jbehave.examples.core.steps", "my.other.steps" ).matchingNames(".*Steps").notMatchingNames(".*SkipSteps");
}

Scanning requires an additional runtime dependency on Reflections:

<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>[version]</version>
</dependency>
It is important to remember that all Steps classes created using the scanning approach must have a default constructor and no dependency injection mechanism is supported. If dependency injection is required, most frameworks also support their own version of class scanning.

Configurable Elements

All configurable elements of Configuration come with a default behaviour, configured in the MostUsefulConfiguration:

Controlling the Embedder using Maven

All the elements of EmbedderControls are also configurable via Maven goals.

It is important to note that JBehave adopts an out-in approach to configuration. Any inner-layer configuration, such as specified in the code, will be overridden by the outer-layer configuration, such as specified by the command-line tools like Maven.