diff --git a/CSF.Screenplay/Reporting/JsonScreenplayReporter.cs b/CSF.Screenplay/Reporting/JsonScreenplayReporter.cs
index 0580da48..1ba28d9d 100644
--- a/CSF.Screenplay/Reporting/JsonScreenplayReporter.cs
+++ b/CSF.Screenplay/Reporting/JsonScreenplayReporter.cs
@@ -118,7 +118,7 @@ void OnRecordAsset(object sender, PerformableAssetEventArgs e)
=> GetPerformanceBuilder(e).RecordAssetForCurrentPerformable(e.FilePath, e.FileSummary);
void OnPerformableFailed(object sender, PerformableFailureEventArgs e)
- => GetPerformanceBuilder(e).RecordFailureForCurrentPerformable(e.Exception);
+ => GetPerformanceBuilder(e).RecordFailureForCurrentPerformable(e.Exception, e.Performable, e.Actor);
void OnPerformableResult(object sender, PerformableResultEventArgs e)
=> GetPerformanceBuilder(e).RecordResultForCurrentPerformable(e.Result);
diff --git a/CSF.Screenplay/Reporting/PerformanceReportBuilder.cs b/CSF.Screenplay/Reporting/PerformanceReportBuilder.cs
index 7ebe38d7..c7238e8b 100644
--- a/CSF.Screenplay/Reporting/PerformanceReportBuilder.cs
+++ b/CSF.Screenplay/Reporting/PerformanceReportBuilder.cs
@@ -169,7 +169,7 @@ public void SpotlightTurnedOff()
/// The actor executing the performable
/// The performance phase in which the performable occurs
///
- ///
+ ///
public void BeginPerformable(object performable, Actor actor, string performancePhase)
{
var performableReport = new PerformableReport
@@ -239,7 +239,7 @@ public void RecordResultForCurrentPerformable(object result)
public void EndPerformable(object performable, Actor actor)
{
CurrentPerformable.Report = performable is ICanReport reporter
- ? reporter.GetReportFragment(actor, formatter).FormattedFragment
+ ? TryGetReport(reporter, actor, formatter)
: string.Format(ReportStrings.FallbackReportFormat, actor.Name, performable.GetType().FullName);
CurrentPerformable.Ended = reportTimer.GetCurrentTime();
performableStack.Pop();
@@ -257,12 +257,25 @@ public void EndPerformable(object performable, Actor actor)
///
///
/// The exception which lead to the failure
- public void RecordFailureForCurrentPerformable(Exception exception)
+ /// The performable which is ending
+ /// The actor which was executing the performable
+ public void RecordFailureForCurrentPerformable(Exception exception, object performable, Actor actor)
{
CurrentPerformable.Exception = exception.ToString();
CurrentPerformable.ExceptionIsFromConsumedPerformable = exception is PerformableException;
- CurrentPerformable.Ended = reportTimer.GetCurrentTime();
- performableStack.Pop();
+ EndPerformable(performable, actor);
+ }
+
+ static string TryGetReport(ICanReport reporter, Actor actor, IFormatsReportFragment formatter)
+ {
+ try
+ {
+ return reporter.GetReportFragment(actor, formatter).FormattedFragment;
+ }
+ catch
+ {
+ return ReportStrings.ReportErrorMessage;
+ }
}
#endregion
diff --git a/CSF.Screenplay/Resources/ReportStrings.cs b/CSF.Screenplay/Resources/ReportStrings.cs
index fbd7fb1e..dd902165 100644
--- a/CSF.Screenplay/Resources/ReportStrings.cs
+++ b/CSF.Screenplay/Resources/ReportStrings.cs
@@ -21,5 +21,8 @@ internal static class ReportStrings
/// Gets a fallback report format string for an actor performing a performable which does not implement .
internal static string FallbackReportFormat => resourceManager.GetString("FallbackReportFormat");
+
+ /// Gets a report text which is recorded when a performable raises an exception from .
+ internal static string ReportErrorMessage => resourceManager.GetString("ReportErrorMessage");
}
}
\ No newline at end of file
diff --git a/CSF.Screenplay/Resources/ReportStrings.restext b/CSF.Screenplay/Resources/ReportStrings.restext
index c4a2a4f9..8cc4aab5 100644
--- a/CSF.Screenplay/Resources/ReportStrings.restext
+++ b/CSF.Screenplay/Resources/ReportStrings.restext
@@ -2,4 +2,5 @@ ActorCreatedFormat = {0} joined the performance
ActorGainedAbilityFormat = {0} is able to {1}
ActorSpotlitFormat = {0} was put into the spotlight
SpotlightTurnedOff = The spotlight was turned off
-FallbackReportFormat = {0} performed {1}
\ No newline at end of file
+FallbackReportFormat = {0} performed {1}
+ReportErrorMessage = An unexpected error occurred getting the report text for this performable
\ No newline at end of file
diff --git a/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs b/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs
index 2a2ea5fe..546fd719 100644
--- a/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs
+++ b/Tests/CSF.Screenplay.Tests/Reporting/PerformanceReportBuilderTests.cs
@@ -195,12 +195,41 @@ public void RecordFailureForCurrentPerformableShouldAddTheExceptionStringToTheRe
string performancePhase)
{
sut.BeginPerformable(performable, actor, performancePhase);
- sut.RecordFailureForCurrentPerformable(new Exception("An error occurred"));
+ sut.RecordFailureForCurrentPerformable(new Exception("An error occurred"), performable, actor);
var report = sut.GetReport(outcome);
Assert.That(report.Reportables,
Has.One.Matches(x => x.Exception.Contains("An error occurred") && x.ExceptionIsFromConsumedPerformable == false));
}
+ [Test, AutoMoqData]
+ public void RecordFailureForCurrentPerformableShouldSetTheReportString(PerformanceReportBuilder sut,
+ [NamedActor("Joe")] Actor actor,
+ bool? outcome,
+ string performable,
+ string performancePhase)
+ {
+ sut.BeginPerformable(performable, actor, performancePhase);
+ sut.RecordFailureForCurrentPerformable(new Exception("An error occurred"), performable, actor);
+ var report = sut.GetReport(outcome);
+ Assert.That(report.Reportables,
+ Has.One.Matches(x => x.Report != null));
+ }
+
+ [Test, AutoMoqData]
+ public void RecordFailureForCurrentPerformableShouldUseAFallbackReportIfGettingTheReportThrows(PerformanceReportBuilder sut,
+ [NamedActor("Joe")] Actor actor,
+ bool? outcome,
+ ICanReport performable,
+ string performancePhase)
+ {
+ Mock.Get(performable).Setup(x => x.GetReportFragment(actor, It.IsAny())).Throws();
+ sut.BeginPerformable(performable, actor, performancePhase);
+ sut.RecordFailureForCurrentPerformable(new Exception("An error occurred"), performable, actor);
+ var report = sut.GetReport(outcome);
+ Assert.That(report.Reportables,
+ Has.One.Matches(x => x.Report == "An unexpected error occurred getting the report text for this performable"));
+ }
+
[Test, AutoMoqData]
public void RecordFailureForCurrentPerformableShouldSetExceptionIsFromConsumedPerformableToTrueIfTheExceptionIsPerformableException(PerformanceReportBuilder sut,
[NamedActor("Joe")] Actor actor,
@@ -209,7 +238,7 @@ public void RecordFailureForCurrentPerformableShouldSetExceptionIsFromConsumedPe
string performancePhase)
{
sut.BeginPerformable(performable, actor, performancePhase);
- sut.RecordFailureForCurrentPerformable(new PerformableException("An error occurred"));
+ sut.RecordFailureForCurrentPerformable(new PerformableException("An error occurred"), performable, actor);
var report = sut.GetReport(outcome);
Assert.That(report.Reportables,
Has.One.Matches(x => x.Exception.Contains("An error occurred") && x.ExceptionIsFromConsumedPerformable == true));