From f08dd0b834bd16032ca8f991f46bf214ccd4d65d Mon Sep 17 00:00:00 2001 From: Robert Coltheart <13191652+robertcoltheart@users.noreply.github.com> Date: Tue, 9 Feb 2021 22:16:19 +0800 Subject: [PATCH 1/2] Add async catch method (#430) --- .../AsyncSpecificationsValueTask.cs | 40 ++++++++++++++ .../CatchSpecs.cs | 52 +++++++++++++++++-- .../Runner/AsyncDelegateRunnerSpecs.cs | 33 ++++++++++++ src/Machine.Specifications/Catch.cs | 52 +++++++++++++++++-- 4 files changed, 170 insertions(+), 7 deletions(-) create mode 100644 src/Examples/Example.Random/AsyncSpecificationsValueTask.cs diff --git a/src/Examples/Example.Random/AsyncSpecificationsValueTask.cs b/src/Examples/Example.Random/AsyncSpecificationsValueTask.cs new file mode 100644 index 000000000..7eae7c6b2 --- /dev/null +++ b/src/Examples/Example.Random/AsyncSpecificationsValueTask.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Machine.Specifications; + +#if !NET45 +namespace Example.Random +{ + public class AsyncSpecificationsValueTask + { + public static int establish_value; + + public static int because_value; + + public static int async_it_value; + + public static int sync_it_value; + + public static int cleanup_value; + + public static ValueTask Test() + { + return new ValueTask(10); + } + + Establish context = async () => + establish_value = await Test(); + + Because of = async () => + because_value = await Test(); + + It should_invoke_sync = () => + sync_it_value = Test().Result; + + It should_invoke_async = async () => + async_it_value = await Test(); + + Cleanup after = async () => + cleanup_value = await Test(); + } +} +#endif diff --git a/src/Machine.Specifications.Specs/CatchSpecs.cs b/src/Machine.Specifications.Specs/CatchSpecs.cs index fa633f7af..f7436716a 100644 --- a/src/Machine.Specifications.Specs/CatchSpecs.cs +++ b/src/Machine.Specifications.Specs/CatchSpecs.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using FluentAssertions; @@ -125,7 +126,52 @@ public class with_a_non_throwing_Action () => ActionSideEffect.Should().Be("hi"); It should_return_null = - () => Result.Should().BeNull(); + () => Result.Should().BeNull(); + } } - } -} \ No newline at end of file + + [Subject(typeof(Catch))] + public class when_calling_catch_with_async_methods + { + static Exception exception; + + [Subject(typeof(Catch))] + public class with_a_non_throwing_action + { + static Task Test() => Task.Run(() => { }); + + Because of = async () => + exception = await Catch.ExceptionAsync(Test); + + It should_return_null = () => + exception.Should().BeNull(); + } + + [Subject(typeof(Catch))] + public class with_a_throwing_action + { + static Task Test() => Task.Run(() => throw new ArgumentNullException()); + + Because of = async () => + exception = await Catch.ExceptionAsync(Test); + + It should_return_exception = () => + exception.Should().BeOfType(); + } + + [Subject(typeof(Catch))] + public class calling_wrong_catch_method + { + static Task Test() => Task.Run(() => throw new ArgumentNullException()); + + Because of = () => + exception = Catch.Exception(() => Catch.Exception(Test)); + + It should_return_exception = () => + exception.Should().BeOfType(); + + It should_contain_message = () => + exception.Message.Should().Be("You must use Catch.ExceptionAsync for async methods"); + } + } +} diff --git a/src/Machine.Specifications.Specs/Runner/AsyncDelegateRunnerSpecs.cs b/src/Machine.Specifications.Specs/Runner/AsyncDelegateRunnerSpecs.cs index be3abfe88..517441496 100644 --- a/src/Machine.Specifications.Specs/Runner/AsyncDelegateRunnerSpecs.cs +++ b/src/Machine.Specifications.Specs/Runner/AsyncDelegateRunnerSpecs.cs @@ -69,4 +69,37 @@ public class when_running_async_specifications_with_exceptions : RunnerSpecs It should_have_failures = () => results.Should().Match(x => x.All(y => !y.Passed)); } + +#if NETCOREAPP + [Subject("Async Delegate Runner")] + public class when_running_async_value_task_specifications : RunnerSpecs + { + Establish context = () => + { + AsyncSpecificationsValueTask.establish_value = 0; + AsyncSpecificationsValueTask.because_value = 0; + AsyncSpecificationsValueTask.async_it_value = 0; + AsyncSpecificationsValueTask.sync_it_value = 0; + AsyncSpecificationsValueTask.cleanup_value = 0; + }; + + Because of = () => + Run(); + + It should_call_establish = () => + AsyncSpecificationsValueTask.establish_value.Should().Be(10); + + It should_call_because = () => + AsyncSpecificationsValueTask.because_value.Should().Be(10); + + It should_call_async_spec = () => + AsyncSpecificationsValueTask.async_it_value.Should().Be(10); + + It should_call_sync_spec = () => + AsyncSpecificationsValueTask.sync_it_value.Should().Be(10); + + It should_call_cleanup = () => + AsyncSpecificationsValueTask.cleanup_value.Should().Be(10); + } +#endif } diff --git a/src/Machine.Specifications/Catch.cs b/src/Machine.Specifications/Catch.cs index db7f913b4..77941cd24 100644 --- a/src/Machine.Specifications/Catch.cs +++ b/src/Machine.Specifications/Catch.cs @@ -1,4 +1,7 @@ using System; +#if !NET35 +using System.Threading.Tasks; +#endif namespace Machine.Specifications { @@ -6,25 +9,66 @@ public static class Catch { public static Exception Exception(Action throwingAction) { - return Only(throwingAction); + try + { + throwingAction(); + } + catch (Exception ex) + { + return ex; + } + + return null; } public static Exception Exception(Func throwingFunc) { +#if !NET35 + Task task; +#endif try { - throwingFunc(); + var result = throwingFunc(); +#if !NET35 + task = result as Task; +#endif } catch (Exception exception) { return exception; } - +#if !NET35 + if (task != null) + { + throw new InvalidOperationException("You must use Catch.ExceptionAsync for async methods"); + } +#endif return null; } +#if !NET35 + public static Task ExceptionAsync(Func throwingAction) + { + Exception exception = null; + + try + { + throwingAction().Wait(); + } + catch (Exception ex) + { + exception = ex; + } + + var source = new TaskCompletionSource(exception); + source.TrySetResult(exception); + + return source.Task; + } +#endif + public static TException Only(Action throwingAction) - where TException : Exception + where TException : Exception { try { From 0edfa774ae77d66cf4129cfbe27109024075a70e Mon Sep 17 00:00:00 2001 From: Robert Coltheart Date: Wed, 10 Feb 2021 21:55:05 +0800 Subject: [PATCH 2/2] Only 4.5 and netstandard supported --- .../TaskSpecificationExtensionsSpecs.cs | 13 +------------ src/Machine.Specifications/Catch.cs | 19 ++++++++----------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/Machine.Specifications.Specs/TaskSpecificationExtensionsSpecs.cs b/src/Machine.Specifications.Specs/TaskSpecificationExtensionsSpecs.cs index 49ba9882f..9ba63ef04 100644 --- a/src/Machine.Specifications.Specs/TaskSpecificationExtensionsSpecs.cs +++ b/src/Machine.Specifications.Specs/TaskSpecificationExtensionsSpecs.cs @@ -50,17 +50,6 @@ public class when_an_async_operation_runs_with_await : AsyncSpecs () => Result.Should().Be("result"); } - [Subject(typeof(TaskSpecificationExtensions), "exception")] - public class when_an_async_operation_fails_without_await : AsyncSpecs - { - static Exception exception; - - Because of = () => exception = Catch.Exception(() => Delayed.Fail()); - - It should_not_capture_the_exception = - () => exception.Should().BeNull(); - } - [Subject(typeof(TaskSpecificationExtensions), "exception")] public class when_a_single_async_operation_fails_with_await : AsyncSpecs { @@ -90,4 +79,4 @@ public class AsyncSpecs { } } -#endif \ No newline at end of file +#endif diff --git a/src/Machine.Specifications/Catch.cs b/src/Machine.Specifications/Catch.cs index 77941cd24..3c00153b2 100644 --- a/src/Machine.Specifications/Catch.cs +++ b/src/Machine.Specifications/Catch.cs @@ -1,5 +1,5 @@ using System; -#if !NET35 +#if !NET35 && !NET40 using System.Threading.Tasks; #endif @@ -23,13 +23,13 @@ public static Exception Exception(Action throwingAction) public static Exception Exception(Func throwingFunc) { -#if !NET35 +#if !NET35 && !NET40 Task task; #endif try { var result = throwingFunc(); -#if !NET35 +#if !NET35 && !NET40 task = result as Task; #endif } @@ -37,7 +37,7 @@ public static Exception Exception(Func throwingFunc) { return exception; } -#if !NET35 +#if !NET35 && !NET40 if (task != null) { throw new InvalidOperationException("You must use Catch.ExceptionAsync for async methods"); @@ -46,24 +46,21 @@ public static Exception Exception(Func throwingFunc) return null; } -#if !NET35 - public static Task ExceptionAsync(Func throwingAction) +#if !NET35 && !NET40 + public static async Task ExceptionAsync(Func throwingAction) { Exception exception = null; try { - throwingAction().Wait(); + await throwingAction(); } catch (Exception ex) { exception = ex; } - var source = new TaskCompletionSource(exception); - source.TrySetResult(exception); - - return source.Task; + return exception; } #endif