Reporting is an essential element of BDD as it allows to monitor the outcome of the stories that have been run. At the heart of JBehave's reporting is the StoryReporter, which is used to report events as they occur.
Currently, the story reporters supported are:
Let's see an example of how we can configure the story reporters. We use the DelegatingStoryReporter as a proxy for the reporters we want to configure, in the example below a plain text reporter (both to System.out and to a file) and an HTML reporter to a file:
For a fully-working example of reporting configuration you may consult the trader example. Note that the configuration only needs to be done in one place, i.e. the parent story class, and not repeated in every story class that maps to a textual story. In general, the specification of the output directory is optional. It is necessary when running with Ant as a workaround for Ant's classloading strategy which uses the Ant Project.class ProtectionDomain, which in turn leads to the $ANT_HOME/lib as the code source location. A better solution would be to override the hacky AntClassLoader behaviour, as also noted by Ant folks in the AntClassLoader#defineClassFromData method. A user-friendly way to do is being investigated.Note that we use the StoryReporterBuilder to implement a builder pattern for file-based reporters via the FilePrintStreamFactory, in which we inject the StoryPathResolver to derive the report file names from the story class, using the same name resolution mechanism used for mapping Java classes and textual stories. So, e.g., if the story class is com.example.MyStory, we'll end up with file report outputs of the form: com.example.my_story.[format] (where format is any of txt,html,xml in the example above).
The file-based print stream factory for the story reporting requires the story class to be provided in order to derive from it the output file paths (with the appropriate format extension). If you want to migrate multiple stories to using file-based reporting, an intermediate stepping stone might be to provide as a story class the parent class in which you build the story reporter (TraderStory.class in the example), which will result in all reporting (for a given format) to be appended to the same file. Then gradually you can refactor your story classes to provide their class to the parent (as the example shows). Otherwise, you may want to keep output in a single file, the choice is yours.The builder provides defaults for all the formats supported, but if the user needs to create a bespoke instance of a reporter for a given format, it can be easily done by overriding the default. E.g. to override the reporter for TXT format to use a ".text" extension (a possibly keywords for a different Locale):
StoryReporter reporter = new StoryReporterBuilder(){ public StoryReporter reporterFor(String storyPath, Format format){ FilePrintStreamFactory factory = new FilePrintStreamFactory(new StoryLocation(storyPath, ouputLocationClass)); switch (format) { case TXT: factory.useConfiguration(new FileConfiguration("text")); return new PrintStreamOutput(factory.getPrintStream(), new Properties(), new LocalizedKeywords(Locale.ITALIAN), true); default: return super.reporterFor(format); } }
The generation of the reports is only the first part of a complete HTML-based reporting solution. Next we generate the stories view, aggregating all the reports that have been configured and written to a given output directory, and presenting a collective index view for all formats configured. Moreover, we can style the reports, both for HTML and non-HTML report formats, by decorating their content with CSS and syntax highlighting. The decorating of non-HTML reports can be switched off if one prefers the raw content accessible via a URL:
Properties properties = new Properties(); properties.setProperty("decorateNonHtml","false"); new StoryReporterBuilder().useViewResources(properties);
The view generation is the responsibility of the ViewGenerator. JBehave provides an implementation (FreemarkerViewGenerator.) based on Freemarker to allow a templateable and easily styleable way to generate the view.
The default resources required for the view generation are bundled in the jbehave-core-resources.zip but can be overridden. The Freemarker templates (the ftl/** files) need to be the classpath for the FreemarkerViewGenerator to find them, while the look and feel resources (the js/* and style/* files) need to be copied to the target/jbehave-reports/view directory (or wherever the index view is generated to) Also note that the default style makes use of images found in the jbehave-site-resources.zip.
The story statistics report above is treated in the view generation slightly differently from other reports, in that the statistics are displayed on the index view page, if available. To ensure they are always available the story reporter builder is configured to have stats as a default format (although the default formats need to be added to the builder).
Default formats allow users to define a builder that can be used in multiple configurations without having to repeat tediously all the formats required, if these are used consistently. By default, "stats" is the only default format (used for collecting reporting statistics). To modify simply override the method withDefaultFormats(). E.g. to add "txt" as a default format:
new StoryReporterBuilder(){ public StoryReporterBuilder withDefaultFormats() { return withFormats(STATS, TXT); } }
By default, JBehave outputs file reports to the directory jbehave-reports (relative to the story class code source location, e.g. the target directory in Maven), but this can be changed via the builder:
new StoryReporterBuilder().withOutputDirectory("my-reports")
In some cases, we may want to set the code location explicitly from a file path:
new StoryReporterBuilder().withCodeLocation(CodeLocations.codeLocationFromPath("build/classes"))
If the default formats or the output file directory are modified via the builder, then correspondingly we need to inform the view generator if these changes. See running stories for details on how to configure these changes.