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));