Skip to content

Commit 9bf03bb

Browse files
author
Jake Ginnivan
committed
Updated markdown report for examples
1 parent 5dc3ec9 commit 9bf03bb

6 files changed

Lines changed: 253 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Story: Account holder withdraws cash
2+
**As an account holder**
3+
**I want to withdraw cash**
4+
**So that I can get money when the bank is closed**
5+
6+
### Example Scenario
7+
Given a <sign> account balance
8+
When the account holder requests money
9+
Then money <action> dispensed
10+
11+
### Examples:
12+
13+
| sign | action | Result | Errors |
14+
| positive | is | Passed | |
15+
| negative | is not | Failed | Step: Then money <action> dispensed failed with exception: [Boom] [Details at 1 below] |
16+
17+
#### Exceptions:
18+
1. Boom
19+
at TestStack.BDDfy.Tests.Reporters.ReportTestData.GetScenarios(Boolean includeFailingScenario, Boolean includeExamples) in ...\ReportTestData.cs:line 113
20+
21+
## Story: Happiness
22+
**As a person**
23+
**I want ice cream**
24+
**So that I can be happy**
25+
26+
### Example Scenario
27+
Given a <sign> account balance
28+
When the account holder requests money
29+
Then money <action> dispensed
30+
31+
### Examples:
32+
33+
| sign | action |
34+
| positive | is |
35+
| negative | is not |

TestStack.BDDfy.Tests/Reporters/MarkDown/MarkDownReportBuilderTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,19 @@ public void ShouldProduceExpectedMarkdown()
1717
var sut = new MarkDownReportBuilder();
1818
ReportApprover.Approve(model, sut);
1919
}
20+
21+
[Test]
22+
[MethodImpl(MethodImplOptions.NoInlining)]
23+
public void ShouldProduceExpectedMarkdownWithExamples()
24+
{
25+
Func<FileReportModel> model = () =>
26+
new FileReportModel(new ReportTestData().CreateTwoStoriesEachWithOneFailingScenarioAndOnePassingScenarioWithThreeStepsOfFiveMillisecondsAndEachHasTwoExamples())
27+
{
28+
RunDate = new DateTime(2014, 3, 25, 11, 30, 5)
29+
};
30+
31+
var sut = new MarkDownReportBuilder();
32+
ReportApprover.Approve(model, sut);
33+
}
2034
}
2135
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
Story: Happiness
2+
As a person
3+
I want ice cream
4+
So that I can be happy
5+
6+
Scenario: Example Scenario
7+
Given a <sign> account balance
8+
When the account holder requests money
9+
Then money <action> dispensed
10+
11+
Examples:
12+
| sign | action |
13+
| positive | is |
14+
| negative | is not |
15+
16+
17+
Story: Account holder withdraws cash
18+
As an account holder
19+
I want to withdraw cash
20+
So that I can get money when the bank is closed
21+
22+
Scenario: Example Scenario
23+
Given a <sign> account balance
24+
When the account holder requests money
25+
Then money <action> dispensed
26+
27+
Examples:
28+
| sign | action | Result | Errors |
29+
| positive | is | Passed | |
30+
| negative | is not | Failed | Step: Then money <action> dispensed failed with exception: [Boom] [Details at 1 below] |
31+
32+
Exceptions:
33+
1. Boom
34+
at TestStack.BDDfy.Tests.Reporters.ReportTestData.GetScenarios(Boolean includeFailingScenario, Boolean includeExamples) in ...\ReportTestData.cs
35+
36+

TestStack.BDDfy.Tests/Reporters/TextReporter/TextReporterTests.cs

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Runtime.CompilerServices;
1+
using System;
2+
using System.Runtime.CompilerServices;
23
using System.Text;
34
using ApprovalTests;
45
using NUnit.Framework;
@@ -25,5 +26,22 @@ public void ShouldProduceExpectedReport()
2526

2627
Approvals.Verify(actual.ToString(), StackTraceScrubber.Scrub);
2728
}
29+
30+
[Test]
31+
[MethodImpl(MethodImplOptions.NoInlining)]
32+
public void ShouldProduceExpectedMarkdownWithExamples()
33+
{
34+
var stories = new ReportTestData().CreateTwoStoriesEachWithOneFailingScenarioAndOnePassingScenarioWithThreeStepsOfFiveMillisecondsAndEachHasTwoExamples();
35+
var actual = new StringBuilder();
36+
37+
foreach (var story in stories)
38+
{
39+
var textReporter = new TextReporter();
40+
textReporter.Process(story);
41+
actual.AppendLine(textReporter.ToString());
42+
}
43+
44+
Approvals.Verify(actual.ToString(), StackTraceScrubber.Scrub);
45+
}
2846
}
2947
}

TestStack.BDDfy.Tests/TestStack.BDDfy.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@
174174
<Content Include="Reporters\Html\ClassicReportBuilderTests.ShouldProduceExpectedHtmlWithExamples.approved.txt" />
175175
<Content Include="Reporters\Html\MetroReportBuilderTests.ShouldProduceExpectedHtmlWithExamples.approved.txt" />
176176
<Content Include="Reporters\MarkDown\MarkDownReportBuilderTests.ShouldProduceExpectedMarkdown.approved.txt" />
177+
<Content Include="Reporters\TextReporter\TextReporterTests.ShouldProduceExpectedMarkdownWithExamples.approved.txt" />
177178
<Content Include="Reporters\TextReporter\TextReporterTests.ShouldProduceExpectedReport.approved.txt" />
178179
</ItemGroup>
179180
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Lines changed: 148 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
using System.Text;
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
25

36
namespace TestStack.BDDfy.Reporters.MarkDown
47
{
58
public class MarkDownReportBuilder : IReportBuilder
69
{
10+
private readonly List<Exception> _exceptions = new List<Exception>();
11+
712
public string CreateReport(FileReportModel model)
813
{
914
var report = new StringBuilder();
1015

1116
foreach (var story in model.Stories)
1217
{
18+
_exceptions.Clear();
1319
if (story.Metadata != null)
1420
{
1521
report.AppendLine(string.Format("## {0}{1}", story.Metadata.TitlePrefix, story.Metadata.Title));
@@ -22,19 +28,154 @@ public string CreateReport(FileReportModel model)
2228
}
2329

2430
report.AppendLine(); // separator
25-
26-
foreach (var scenario in story.Scenarios)
31+
32+
foreach (var scenarioGroup in story.Scenarios.GroupBy(s => s.Id))
2733
{
28-
report.AppendLine(string.Format("### {0}", scenario.Title));
34+
if (scenarioGroup.Count() > 1)
35+
{
36+
// all scenarios in an example based scenario share the same header and narrative
37+
var exampleScenario = story.Scenarios.First();
38+
report.AppendLine(string.Format("### {0}", exampleScenario.Title));
39+
40+
if (exampleScenario.Steps.Any())
41+
{
42+
foreach (var step in exampleScenario.Steps.Where(s => s.ShouldReport))
43+
report.AppendLine(" " + step.Title + " ");
44+
}
2945

30-
foreach (var step in scenario.Steps)
31-
report.AppendLine(" " + step.Title + " ");
46+
report.AppendLine(); // separator
47+
WriteExamples(report, exampleScenario, scenarioGroup);
48+
}
49+
else
50+
{
51+
foreach (var scenario in scenarioGroup)
52+
{
53+
report.AppendLine(string.Format("### {0}", scenario.Title));
3254

33-
report.AppendLine(); // separator
55+
foreach (var step in scenario.Steps)
56+
report.AppendLine(" " + step.Title + " ");
57+
58+
report.AppendLine(); // separator
59+
}
60+
}
3461
}
62+
63+
ReportExceptions(report);
3564
}
65+
_exceptions.Clear();
3666

3767
return report.ToString();
3868
}
69+
70+
private void WriteExamples(StringBuilder report, Scenario exampleScenario, IEnumerable<Scenario> scenarioGroup)
71+
{
72+
report.AppendLine("### Examples: ");
73+
report.AppendLine();
74+
var scenarios = scenarioGroup.ToArray();
75+
var allPassed = scenarios.All(s => s.Result == Result.Passed);
76+
var exampleColumns = exampleScenario.Example.Headers.Length;
77+
var numberColumns = allPassed ? exampleColumns : exampleColumns + 2;
78+
var maxWidth = new int[numberColumns];
79+
var rows = new List<string[]>();
80+
81+
Action<IEnumerable<string>, string, string> addRow = (cells, result, error) =>
82+
{
83+
var row = new string[numberColumns];
84+
var index = 0;
85+
86+
foreach (var cellText in cells)
87+
row[index++] = cellText;
88+
89+
if (!allPassed)
90+
{
91+
row[numberColumns - 2] = result;
92+
row[numberColumns - 1] = error;
93+
}
94+
95+
for (var i = 0; i < numberColumns; i++)
96+
{
97+
var rowValue = row[i];
98+
if (rowValue != null && rowValue.Length > maxWidth[i])
99+
maxWidth[i] = rowValue.Length;
100+
}
101+
102+
rows.Add(row);
103+
};
104+
105+
addRow(exampleScenario.Example.Headers, "Result", "Errors");
106+
foreach (var scenario in scenarios)
107+
{
108+
var failingStep = scenario.Steps.FirstOrDefault(s => s.Result == Result.Failed);
109+
var error = failingStep == null
110+
? null
111+
: string.Format("Step: {0} failed with exception: {1}", failingStep.Title, CreateExceptionMessage(failingStep));
112+
113+
addRow(scenario.Example.Values.Select(e => e.GetValueAsString()), scenario.Result.ToString(), error);
114+
}
115+
116+
foreach (var row in rows)
117+
WriteExampleRow(report, row, maxWidth);
118+
}
119+
120+
private void WriteExampleRow(StringBuilder report, string[] row, int[] maxWidth)
121+
{
122+
report.Append(" ");
123+
for (int index = 0; index < row.Length; index++)
124+
{
125+
var col = row[index];
126+
report.AppendFormat("| {0} ", (col ?? string.Empty).Trim().PadRight(maxWidth[index]));
127+
}
128+
report.AppendLine("|");
129+
}
130+
131+
private string CreateExceptionMessage(Step step)
132+
{
133+
_exceptions.Add(step.Exception);
134+
135+
var exceptionReference = string.Format("[Details at {0} below]", _exceptions.Count);
136+
if (!string.IsNullOrEmpty(step.Exception.Message))
137+
return string.Format("[{0}] {1}", FlattenExceptionMessage(step.Exception.Message), exceptionReference);
138+
139+
return string.Format("{0}", exceptionReference);
140+
}
141+
142+
void ReportExceptions(StringBuilder report)
143+
{
144+
if (_exceptions.Count == 0)
145+
return;
146+
147+
report.AppendLine();
148+
report.Append("#### Exceptions:");
149+
150+
for (int index = 0; index < _exceptions.Count; index++)
151+
{
152+
var exception = _exceptions[index];
153+
report.AppendLine();
154+
report.AppendFormat(" {0}. ", index + 1);
155+
156+
if (!string.IsNullOrEmpty(exception.Message))
157+
{
158+
report.AppendLine(FlattenExceptionMessage(exception.Message));
159+
}
160+
else
161+
report.AppendLine();
162+
163+
var stackTrace = string.Join(Environment.NewLine, exception.StackTrace.Split(new[] { Environment.NewLine }, StringSplitOptions.None)
164+
.Select(s => " " + s));
165+
report.AppendLine(stackTrace);
166+
}
167+
168+
report.AppendLine();
169+
}
170+
171+
static string FlattenExceptionMessage(string message)
172+
{
173+
return message
174+
.Replace("\t", " ") // replace tab with one space
175+
.Replace(Environment.NewLine, ", ") // replace new line with one space
176+
.Trim() // trim starting and trailing spaces
177+
.Replace(" ", " ")
178+
.TrimEnd(','); // chop any , from the end
179+
}
39180
}
40181
}

0 commit comments

Comments
 (0)