- Revision
- 1471
- Author
- mauro
- Date
- 2009-12-26 05:11:17 -0600 (Sat, 26 Dec 2009)
Log Message
JBEHAVE-228: Added ScenarioReporterBuilder. Updated trader example and PrintStreamScenarioReporterBehaviour to use it. Updated documentation.
Modified Paths
- trunk/core/distribution/src/site/content/reports.html
- trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/TraderScenario.java
- trunk/core/jbehave-core/src/behaviour/java/org/jbehave/scenario/reporters/PrintStreamScenarioReporterBehaviour.java
- trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/FilePrintStreamFactory.java
Added Paths
Diff
Modified: trunk/core/distribution/src/site/content/reports.html (1470 => 1471)
--- trunk/core/distribution/src/site/content/reports.html 2009-12-24 17:50:15 UTC (rev 1470) +++ trunk/core/distribution/src/site/content/reports.html 2009-12-26 11:11:17 UTC (rev 1471) @@ -53,29 +53,23 @@ @Override public ScenarioReporter forReportingScenarios() { - return new DelegatingScenarioReporter( - // report to System.out - new PrintStreamScenarioReporter(), - // report to .txt file in PLAIN format - new PrintStreamScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("txt")).getPrintStream()), - // report to .html file in HTML format - new HtmlPrintStreamScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("html")).getPrintStream()), - // report to .xml file in XML format - new XmlPrintStreamScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("xml")).getPrintStream()), - // report to .stats file in Properties format - new StatisticsScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("stats")).getPrintStream())); + return new ScenarioReporterBuilder(new FilePrintStreamFactory(scenarioClass, converter)) + .with(CONSOLE) // report to System.out + .with(STATS) // report to .stats file in Properties format + .with(TXT) // report to .txt file in PLAIN format + .with(HTML) // report to .html file in HTML format + .with(XML) // report to .xml file in XML format + .build(); } }, new TraderSteps()); } </pre> -<p>Note that for the file-based reporters we use the <a - href="" +<p>Note that we use the <a + href="" +to implement a builder pattern for file-based reporters via the <a + href="" in which we inject the <a href="" to derive the report file names from the scenario class, using the same @@ -84,6 +78,22 @@ we'll end up with file report outputs of the form: <b>com.example.my_scenario.[format]</b> (where <b>format</b> is any of <b>txt,html,xml,stats</b> in the example above).</p> +<p>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 <b>TXT</b> format +to use a ".text" extension (a possibly keywords for a different Locale):</p> +<pre class="brush: java"> + + ScenarioReporter reporter = new ScenarioReporterBuilder(factory){ + public ScenarioReporter reporterFor(Format format){ + switch (format) { + case TXT: + factory.useConfiguration(new FileConfiguration("text")); + return new PrintStreamScenarioReporter(factory.getPrintStream(), new Properties(), new I18nKeyWords(Locale.ITALIAN), true); + default: + return super.reporterFor(format); + } + } +</pre> <h2>Report Rendering</h2> <p>The generation of the reports is only the first part of a
Modified: trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/TraderScenario.java (1470 => 1471)
--- trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/TraderScenario.java 2009-12-24 17:50:15 UTC (rev 1470) +++ trunk/core/examples/trader/src/main/java/org/jbehave/examples/trader/TraderScenario.java 2009-12-26 11:11:17 UTC (rev 1471) @@ -1,5 +1,11 @@ package org.jbehave.examples.trader; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.CONSOLE; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.HTML; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.STATS; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.TXT; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.XML; + import org.jbehave.scenario.JUnitScenario; import org.jbehave.scenario.PropertyBasedConfiguration; import org.jbehave.scenario.RunnableScenario; @@ -8,14 +14,9 @@ import org.jbehave.scenario.parser.ScenarioDefiner; import org.jbehave.scenario.parser.ScenarioNameResolver; import org.jbehave.scenario.parser.UnderscoredCamelCaseResolver; -import org.jbehave.scenario.reporters.DelegatingScenarioReporter; import org.jbehave.scenario.reporters.FilePrintStreamFactory; -import org.jbehave.scenario.reporters.HtmlPrintStreamScenarioReporter; -import org.jbehave.scenario.reporters.PrintStreamScenarioReporter; import org.jbehave.scenario.reporters.ScenarioReporter; -import org.jbehave.scenario.reporters.StatisticsScenarioReporter; -import org.jbehave.scenario.reporters.XmlPrintStreamScenarioReporter; -import org.jbehave.scenario.reporters.FilePrintStreamFactory.FileConfiguration; +import org.jbehave.scenario.reporters.ScenarioReporterBuilder; public class TraderScenario extends JUnitScenario { @@ -30,22 +31,13 @@ @Override public ScenarioReporter forReportingScenarios() { - return new DelegatingScenarioReporter( - // report to System.out - new PrintStreamScenarioReporter(), - // report to .txt file in PLAIN format - new PrintStreamScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("txt")).getPrintStream()), - // report to .html file in HTML format - new HtmlPrintStreamScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("html")).getPrintStream()), - // report to .xml file in XML format - new XmlPrintStreamScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("xml")).getPrintStream()), - // report to .stats file in Properties format - new StatisticsScenarioReporter(new FilePrintStreamFactory(scenarioClass, converter, - new FileConfiguration("stats")).getPrintStream())); - + return new ScenarioReporterBuilder(new FilePrintStreamFactory(scenarioClass, converter)) + .with(CONSOLE) + .with(STATS) + .with(TXT) + .with(HTML) + .with(XML) + .build(); } }, new TraderSteps());
Modified: trunk/core/jbehave-core/src/behaviour/java/org/jbehave/scenario/reporters/PrintStreamScenarioReporterBehaviour.java (1470 => 1471)
--- trunk/core/jbehave-core/src/behaviour/java/org/jbehave/scenario/reporters/PrintStreamScenarioReporterBehaviour.java 2009-12-24 17:50:15 UTC (rev 1470) +++ trunk/core/jbehave-core/src/behaviour/java/org/jbehave/scenario/reporters/PrintStreamScenarioReporterBehaviour.java 2009-12-26 11:11:17 UTC (rev 1471) @@ -3,6 +3,9 @@ import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.jbehave.Ensure.ensureThat; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.HTML; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.STATS; +import static org.jbehave.scenario.reporters.ScenarioReporterBuilder.Format.TXT; import static org.junit.Assert.assertEquals; import java.io.ByteArrayOutputStream; @@ -46,11 +49,11 @@ + "When I request $20\n" + "When I ask Liz for a loan of $100\n" + "Then I should have a balance of $30 (PENDING)\n" + "Then I should have $20 (NOT PERFORMED)\n" + "Then I don't return loan (FAILED)\n" + "Examples:\n\n" + "|money|to|\n" + "|$30|Mauro|\n" - + "|$50|Paul|\n" + "\n\n" + // Examples table - "\nExample: {to=Mauro, money=$30}\n" + "\nExample: {to=Paul, money=$50}\n" + "\n" + // end - // of - // examples - "\n\n"; // end of scenario and story + + "|$50|Paul|\n" + "\n\n" // Examples table + + "\nExample: {to=Mauro, money=$30}\n" + "\nExample: {to=Paul, money=$50}\n" + "\n" // end + // of + // examples + + "\n\n"; // end of scenario and story ensureThatOutputIs(out, expected); } @@ -129,7 +132,7 @@ + "</div><!-- after scenario -->\n" + "</div><!-- after story -->\n"; ensureThatOutputIs(out, expected); } - + @Test public void shouldReportEventsToXmlPrintStream() { // Given @@ -146,31 +149,25 @@ narrateAnInterestingStory(reporter); // Then - String expected = - "<story path=\"/path/to/story\" title=\"An interesting story\">\n" + - "<scenario keyword=\"Scenario:\" title=\"I ask for a loan\">\n" + - "<givenScenarios keyword=\"GivenScenarios:\"paths=\"[/given/scenario1,/given/scenario2]\"</givenScenarios>\n" + - "<step outcome=\"successful\">Given I have a balance of $50</step>\n" + - "<step outcome=\"successful\">When I request $20</step>\n" + - "<step outcome=\"successful\">When I ask Liz for a loan of $100</step>\n" + - "<step outcome=\"pending\" keyword=\"PENDING\">Then I should have a balance of $30</step>\n" + - "<step outcome=\"notPerformed\" keyword=\"NOT PERFORMED\">Then I should have $20</step>\n" + - "<step outcome=\"failed\" keyword=\"FAILED\">Then I don't return loan</step>\n" + - "<examples keyword=\"Examples:\">\n" + - "<parameters>\n" + - "<names><name>money</name><name>to</name></names>\n" + - "<values><value>$30</value><value>Mauro</value></values>\n" + - "<values><value>$50</value><value>Paul</value></values>\n" + - "</parameters>\n" + - "\n<example keyword=\"Example:\">{to=Mauro, money=$30}</example>\n" + - "\n<example keyword=\"Example:\">{to=Paul, money=$50}</example>\n" + - "</examples>\n" + - "</scenario>\n" + - "</story>\n"; + String expected = "<story path=\"/path/to/story\" title=\"An interesting story\">\n" + + "<scenario keyword=\"Scenario:\" title=\"I ask for a loan\">\n" + + "<givenScenarios keyword=\"GivenScenarios:\"paths=\"[/given/scenario1,/given/scenario2]\"</givenScenarios>\n" + + "<step outcome=\"successful\">Given I have a balance of $50</step>\n" + + "<step outcome=\"successful\">When I request $20</step>\n" + + "<step outcome=\"successful\">When I ask Liz for a loan of $100</step>\n" + + "<step outcome=\"pending\" keyword=\"PENDING\">Then I should have a balance of $30</step>\n" + + "<step outcome=\"notPerformed\" keyword=\"NOT PERFORMED\">Then I should have $20</step>\n" + + "<step outcome=\"failed\" keyword=\"FAILED\">Then I don't return loan</step>\n" + + "<examples keyword=\"Examples:\">\n" + "<parameters>\n" + + "<names><name>money</name><name>to</name></names>\n" + + "<values><value>$30</value><value>Mauro</value></values>\n" + + "<values><value>$50</value><value>Paul</value></values>\n" + "</parameters>\n" + + "\n<example keyword=\"Example:\">{to=Mauro, money=$30}</example>\n" + + "\n<example keyword=\"Example:\">{to=Paul, money=$50}</example>\n" + "</examples>\n" + + "</scenario>\n" + "</story>\n"; ensureThatOutputIs(out, expected); } - private void narrateAnInterestingStory(ScenarioReporter reporter) { StoryDefinition story = new StoryDefinition(new Blurb("An interesting story"), new ArrayList<ScenarioDefinition>(), "/path/to/story"); @@ -197,7 +194,7 @@ private void ensureThatOutputIs(OutputStream out, String expected) { // JUnit assertion allows easier comparison of strings in IDE assertEquals(expected, dos2unix(out.toString())); - //ensureThat(out.toString(), equalTo(expected)); + // ensureThat(out.toString(), equalTo(expected)); } private String dos2unix(String string) { @@ -302,16 +299,16 @@ @Test public void shouldCreateAndWriteToFilePrintStreamForScenarioClass() throws IOException { - UnderscoredCamelCaseResolver converter = new UnderscoredCamelCaseResolver(".scenario"); // Given Class<MyScenario> scenarioClass = MyScenario.class; - File file = fileFor(scenarioClass, converter); + ScenarioNameResolver converter = new UnderscoredCamelCaseResolver(".scenario"); + FilePrintStreamFactory factory = new FilePrintStreamFactory(scenarioClass, converter); + File file = factory.getOutputFile(); file.delete(); ensureThat(!file.exists()); // When - FilePrintStreamFactory factory = new FilePrintStreamFactory(scenarioClass, converter); PrintStream printStream = factory.getPrintStream(); printStream.print("Hello World"); @@ -320,41 +317,54 @@ ensureThat(IOUtils.toString(new FileReader(file)), equalTo("Hello World")); } - private File fileFor(Class<MyScenario> scenarioClass, UnderscoredCamelCaseResolver converter) { - FileConfiguration configuration = new FileConfiguration(); - File outputDirectory = FilePrintStreamFactory.outputDirectory(scenarioClass, configuration); - String fileName = FilePrintStreamFactory.fileName(scenarioClass, converter, configuration); - return new File(outputDirectory, fileName); - } - @Test public void shouldReportEventsToFilePrintStreamsAndRenderAggregatedIndex() throws IOException { + Class<MyScenario> scenarioClass = MyScenario.class; ScenarioNameResolver nameResolver = new UnderscoredCamelCaseResolver(); - Class<MyScenario> scenarioClass = MyScenario.class; - ScenarioReporter htmlReporter = new HtmlPrintStreamScenarioReporter(new FilePrintStreamFactory( - scenarioClass, nameResolver, new FileConfiguration("html")).getPrintStream()); - ScenarioReporter statsReporter = new StatisticsScenarioReporter(new FilePrintStreamFactory( - scenarioClass, nameResolver, new FileConfiguration("stats")).getPrintStream()); - ScenarioReporter txtReporter = new PrintStreamScenarioReporter(new FilePrintStreamFactory( - scenarioClass, nameResolver, new FileConfiguration("txt")).getPrintStream()); - ScenarioReporter reporter = new DelegatingScenarioReporter(htmlReporter, statsReporter, txtReporter); + FilePrintStreamFactory printStreamFactory = new FilePrintStreamFactory(scenarioClass, nameResolver); + ScenarioReporter reporter = new ScenarioReporterBuilder(printStreamFactory).with(HTML).with(TXT).with(STATS) + .build(); // When narrateAnInterestingStory(reporter); - File outputDirectory = new FilePrintStreamFactory(scenarioClass, new UnderscoredCamelCaseResolver()) - .getOutputDirectory(); + File outputDirectory = printStreamFactory.getOutputFile().getParentFile(); ReportRenderer renderer = new FreemarkerReportRenderer(); - renderer.render(outputDirectory, asList("html", "stats", "txt")); + renderer.render(outputDirectory, asList("html", "txt", "stats")); // Then ensureFileExists(new File(outputDirectory, "rendered/index.html")); } + @Test + public void shouldBuildPrintStreamReportersAndOverrideDefaultForAGivenFormat() throws IOException { + Class<MyScenario> scenarioClass = MyScenario.class; + ScenarioNameResolver nameResolver = new UnderscoredCamelCaseResolver(); + FilePrintStreamFactory factory = new FilePrintStreamFactory(scenarioClass, nameResolver); + ScenarioReporter reporter = new ScenarioReporterBuilder(factory){ + public ScenarioReporter reporterFor(Format format){ + switch (format) { + case TXT: + factory.useConfiguration(new FileConfiguration("text")); + return new PrintStreamScenarioReporter(factory.getPrintStream(), new Properties(), new I18nKeyWords(), true); + default: + return super.reporterFor(format); + } + } + }.with(TXT).build(); + + // When + narrateAnInterestingStory(reporter); + + // Then + File outputFile = factory.getOutputFile(); + ensureFileExists(outputFile); + } + private void ensureFileExists(File renderedOutput) throws IOException, FileNotFoundException { ensureThat(renderedOutput.exists()); ensureThat(IOUtils.toString(new FileReader(renderedOutput)).length() > 0); - } - + } + @Test(expected = RenderingFailedException.class) public void shouldFailRenderingOutputWithInexistentTemplates() throws IOException { // Given
Modified: trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/FilePrintStreamFactory.java (1470 => 1471)
--- trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/FilePrintStreamFactory.java 2009-12-24 17:50:15 UTC (rev 1470) +++ trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/FilePrintStreamFactory.java 2009-12-26 11:11:17 UTC (rev 1471) @@ -14,8 +14,11 @@ */ public class FilePrintStreamFactory implements PrintStreamFactory { - private File outputDirectory; private PrintStream printStream; + private Class<? extends RunnableScenario> scenarioClass; + private ScenarioNameResolver scenarioNameResolver; + private FileConfiguration configuration; + private File outputFile; public FilePrintStreamFactory(Class<? extends RunnableScenario> scenarioClass, ScenarioNameResolver scenarioNameResolver) { @@ -24,35 +27,49 @@ public FilePrintStreamFactory(Class<? extends RunnableScenario> scenarioClass, ScenarioNameResolver scenarioNameResolver, FileConfiguration configuration) { - this(outputDirectory(scenarioClass, configuration), - fileName(scenarioClass, scenarioNameResolver, configuration)); + this.scenarioClass = scenarioClass; + this.scenarioNameResolver = scenarioNameResolver; + this.configuration = configuration; + this.outputFile = outputFile(scenarioClass, scenarioNameResolver, this.configuration); } - public FilePrintStreamFactory(File outputDirectory, String fileName) { - this.outputDirectory = outputDirectory; - outputDirectory.mkdirs(); + public FilePrintStreamFactory(File outputFile) { + this.outputFile = outputFile; + } + + public PrintStream getPrintStream() { try { - printStream = new PrintStream(new FileOutputStream(new File(outputDirectory, fileName), true)); + outputFile.getParentFile().mkdirs(); + printStream = new PrintStream(new FileOutputStream(outputFile, true)); } catch (FileNotFoundException e) { throw new RuntimeException(e); } + return printStream; } - public PrintStream getPrintStream() { - return printStream; + public File getOutputFile() { + return outputFile; } - public File getOutputDirectory() { - return outputDirectory; + public void useConfiguration(FileConfiguration configuration) { + this.configuration = configuration; + this.outputFile = outputFile(scenarioClass, scenarioNameResolver, configuration); } - static File outputDirectory(Class<? extends RunnableScenario> scenarioClass, FileConfiguration configuration) { + protected File outputFile(Class<? extends RunnableScenario> scenarioClass, ScenarioNameResolver scenarioNameResolver, + FileConfiguration configuration) { + File outputDirectory = outputDirectory(scenarioClass, configuration); + String fileName = fileName(scenarioClass, scenarioNameResolver, configuration); + return new File(outputDirectory, fileName); + } + + protected File outputDirectory(Class<? extends RunnableScenario> scenarioClass, FileConfiguration configuration) { String classesDir = scenarioClass.getProtectionDomain().getCodeSource().getLocation().getFile(); File targetDirectory = new File(classesDir).getParentFile(); return new File(targetDirectory, configuration.getDirectory()); } - static String fileName(Class<? extends RunnableScenario> scenarioClass, ScenarioNameResolver scenarioNameResolver, + protected String fileName(Class<? extends RunnableScenario> scenarioClass, ScenarioNameResolver scenarioNameResolver, FileConfiguration configuration) { String scenarioName = scenarioNameResolver.resolve(scenarioClass).replace('/', '.'); String name = scenarioName.substring(0, scenarioName.lastIndexOf(".")); @@ -94,4 +111,5 @@ } } + }
Added: trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/ScenarioReporterBuilder.java (0 => 1471)
--- trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/ScenarioReporterBuilder.java (rev 0) +++ trunk/core/jbehave-core/src/main/java/org/jbehave/scenario/reporters/ScenarioReporterBuilder.java 2009-12-26 11:11:17 UTC (rev 1471) @@ -0,0 +1,94 @@ +package org.jbehave.scenario.reporters; + +import java.util.HashMap; +import java.util.Map; + +import org.jbehave.scenario.reporters.FilePrintStreamFactory.FileConfiguration; + +/** + * <p> + * A <a href="" for + * {...@link ScenarioReporter}s. It builds a {...@link DelegatingScenarioReporter} + * with delegates for a number of formats - mostly file-based ones except + * {[email protected]}. It requires a + * {...@link FilePrintStreamFactory} and provides default delegate instances for + * each format. + * </p> + * <p> + * To build reporter with default delegates for given formats: + * + * <pre> + * Class<MyScenario> scenarioClass = MyScenario.class; + * ScenarioNameResolver nameResolver = new UnderscoredCamelCaseResolver(); + * FilePrintStreamFactory printStreamFactory = new FilePrintStreamFactory(scenarioClass, nameResolver); + * ScenarioReporter reporter = new ScenarioReporterBuilder(printStreamFactory).with(HTML).with(TXT).with(STATS).build(); + * </pre> + * </p> + * <p>To override the default instance of a given reporter delegate, e.g. to report format <b>TXT</b> to <b>.text</b> files + * and to inject other non-default parameters, such as keywords for a different locale: + * <pre> + * ScenarioReporter reporter = new ScenarioReporterBuilder(printStreamFactory){ + * public ScenarioReporter reporterFor(Format format){ + * switch (format) { + * case TXT: + * factory.useConfiguration(new FileConfiguration("text")); + * return new PrintStreamScenarioReporter(factory.getPrintStream(), new Properties(), new I18nKeywords(Locale), true); + * default: + * return super.reporterFor(format); + * } + * } + * </pre> + */ +public class ScenarioReporterBuilder { + + public enum Format { + CONSOLE, STATS, TXT, HTML, XML + }; + + protected final FilePrintStreamFactory factory; + protected Map<Format, ScenarioReporter> delegates = new HashMap<Format, ScenarioReporter>(); + + public ScenarioReporterBuilder(FilePrintStreamFactory factory) { + this.factory = factory; + } + + public ScenarioReporter build() { + return new DelegatingScenarioReporter(delegates.values()); + } + + public ScenarioReporterBuilder with(Format format) { + delegates.put(format, reporterFor(format)); + return this; + } + + public ScenarioReporter reporterFor(Format format) { + switch (format) { + case CONSOLE: + return new PrintStreamScenarioReporter(); + case STATS: + factory.useConfiguration(new FileConfiguration("stats")); + return new StatisticsScenarioReporter(factory.getPrintStream()); + case TXT: + factory.useConfiguration(new FileConfiguration("txt")); + return new PrintStreamScenarioReporter(factory.getPrintStream()); + case HTML: + factory.useConfiguration(new FileConfiguration("html")); + return new HtmlPrintStreamScenarioReporter(factory.getPrintStream()); + case XML: + factory.useConfiguration(new FileConfiguration("xml")); + return new XmlPrintStreamScenarioReporter(factory.getPrintStream()); + default: + throw new UnsupportedReporterFormatException(format); + } + } + + @SuppressWarnings("serial") + public static class UnsupportedReporterFormatException extends RuntimeException { + + public UnsupportedReporterFormatException(Format format) { + super("Building ScenarioReporter not supported for format " + format); + } + + } + +}
To unsubscribe from this list please visit:
