From f563067172ecc8a40fe072430a595cc4ac0aede7 Mon Sep 17 00:00:00 2001 From: Daniel Wertheim Date: Sat, 20 Feb 2021 22:36:26 +0100 Subject: [PATCH] Opens up for more control of the exception being produced (#153) Replaces #116 --- src/projects/EnsureThat/Ensure.cs | 11 +++-- src/projects/EnsureThat/EnsureOptions.cs | 27 +++++++++++-- .../EnsureThat/Internals/ExceptionFactory.cs | 13 +++--- .../EnsureThat/Internals/IExceptionFactory.cs | 12 ++++++ src/tests/UnitTests/OptsTests.cs | 40 ++++++++++++++++++- src/tests/UnitTests/UnitTests.csproj | 2 +- 6 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 src/projects/EnsureThat/Internals/IExceptionFactory.cs diff --git a/src/projects/EnsureThat/Ensure.cs b/src/projects/EnsureThat/Ensure.cs index bc7a45a9..40c926df 100644 --- a/src/projects/EnsureThat/Ensure.cs +++ b/src/projects/EnsureThat/Ensure.cs @@ -7,7 +7,10 @@ namespace EnsureThat { public static class Ensure { - internal static readonly ExceptionFactory ExceptionFactory = new ExceptionFactory(); + /// + /// Gets or Sets the Exception factory to use. + /// + public static IExceptionFactory ExceptionFactory { get; set; } = new ExceptionFactory(); /// /// Ensures for objects. @@ -76,7 +79,7 @@ public static class Ensure /// /// [Pure] - public static Param That([NoEnumeration]T value, string name = null, OptsFn optsFn = null) + public static Param That([NoEnumeration] T value, string name = null, OptsFn optsFn = null) => new Param(name, value, optsFn); /// @@ -90,7 +93,7 @@ public static Param That([NoEnumeration]T value, string name = null, OptsF /// /// [Pure] - public static StringParam That([NoEnumeration]string value, string name = null, OptsFn optsFn = null) + public static StringParam That([NoEnumeration] string value, string name = null, OptsFn optsFn = null) => new StringParam(name, value, optsFn); /// @@ -122,4 +125,4 @@ public static TypeParam ThatTypeFor([NotNull] T value, string name = null, Op public static TypeParam ThatType([NotNull] Type value, string name = null, OptsFn optsFn = null) => new TypeParam(name, value, optsFn); } -} \ No newline at end of file +} diff --git a/src/projects/EnsureThat/EnsureOptions.cs b/src/projects/EnsureThat/EnsureOptions.cs index 42595932..61e74a93 100644 --- a/src/projects/EnsureThat/EnsureOptions.cs +++ b/src/projects/EnsureThat/EnsureOptions.cs @@ -2,23 +2,42 @@ namespace EnsureThat { + public delegate Exception CustomExceptionFactory(string message, string paramName); + public struct EnsureOptions { /// - /// If defined, this exception will be thrown instead of the - /// standard exceptions for the particular ensure method. + /// If defined, this factory will be used to produce the exception that + /// will be thrown instead of the standard exceptions for the particular + /// ensure method. + /// Assign using . + /// + public CustomExceptionFactory CustomExceptionFactory { get; private set; } + + /// + /// If defined, and no has been defined, + /// this exception will be thrown instead of the standard exceptions for the + /// particular ensure method. /// Assign using . /// public Exception CustomException { get; private set; } /// - /// If defined, and no has been defined, + /// If defined, and neither + /// nor has been defined, /// this message will be used instead of the standard message for the /// particular ensure method. /// Assign using . /// public string CustomMessage { get; private set; } + public EnsureOptions WithExceptionFactory(CustomExceptionFactory factory) + { + CustomExceptionFactory = factory; + + return this; + } + public EnsureOptions WithException(Exception ex) { CustomException = ex; @@ -33,4 +52,4 @@ public EnsureOptions WithMessage(string message) return this; } } -} \ No newline at end of file +} diff --git a/src/projects/EnsureThat/Internals/ExceptionFactory.cs b/src/projects/EnsureThat/Internals/ExceptionFactory.cs index 047a2dce..8b0fc879 100644 --- a/src/projects/EnsureThat/Internals/ExceptionFactory.cs +++ b/src/projects/EnsureThat/Internals/ExceptionFactory.cs @@ -3,11 +3,11 @@ namespace EnsureThat.Internals { - internal sealed class ExceptionFactory + internal sealed class ExceptionFactory : IExceptionFactory { [NotNull] [Pure] - internal Exception ArgumentException([NotNull] string defaultMessage, string paramName, OptsFn optsFn = null) + public Exception ArgumentException(string defaultMessage, string paramName, OptsFn optsFn = null) { if (optsFn != null) { @@ -25,12 +25,15 @@ internal Exception ArgumentException([NotNull] string defaultMessage, string par [NotNull] [Pure] - internal Exception ArgumentNullException([NotNull] string defaultMessage, string paramName, OptsFn optsFn = null) + public Exception ArgumentNullException(string defaultMessage, string paramName, OptsFn optsFn = null) { if (optsFn != null) { var opts = optsFn(new EnsureOptions()); + if (opts.CustomExceptionFactory != null) + return opts.CustomExceptionFactory(defaultMessage, paramName); + if (opts.CustomException != null) return opts.CustomException; @@ -43,7 +46,7 @@ internal Exception ArgumentNullException([NotNull] string defaultMessage, string [NotNull] [Pure] - internal Exception ArgumentOutOfRangeException([NotNull] string defaultMessage, string paramName, TValue value, OptsFn optsFn = null) + public Exception ArgumentOutOfRangeException(string defaultMessage, string paramName, TValue value, OptsFn optsFn = null) { if (optsFn != null) { @@ -59,4 +62,4 @@ internal Exception ArgumentOutOfRangeException([NotNull] string defaultM return new ArgumentOutOfRangeException(paramName, value, defaultMessage); } } -} \ No newline at end of file +} diff --git a/src/projects/EnsureThat/Internals/IExceptionFactory.cs b/src/projects/EnsureThat/Internals/IExceptionFactory.cs new file mode 100644 index 00000000..ce41ba3d --- /dev/null +++ b/src/projects/EnsureThat/Internals/IExceptionFactory.cs @@ -0,0 +1,12 @@ +using System; +using JetBrains.Annotations; + +namespace EnsureThat.Internals +{ + public interface IExceptionFactory + { + Exception ArgumentException([NotNull] string defaultMessage, string paramName, OptsFn optsFn = null); + Exception ArgumentNullException([NotNull] string defaultMessage, string paramName, OptsFn optsFn = null); + Exception ArgumentOutOfRangeException([NotNull] string defaultMessage, string paramName, TValue value, OptsFn optsFn = null); + } +} diff --git a/src/tests/UnitTests/OptsTests.cs b/src/tests/UnitTests/OptsTests.cs index 27da7e95..33d6bd03 100644 --- a/src/tests/UnitTests/OptsTests.cs +++ b/src/tests/UnitTests/OptsTests.cs @@ -10,6 +10,44 @@ namespace UnitTests { public class OptsTests { + public class WithCustomExceptionFactoryTests : UnitTestBase + { + [Fact] + public void ThrowsTheCustomExceptionFromTheFactory() + { + object value = null; + OptsFn options = o => o.WithExceptionFactory((_, __) => new KeyNotFoundException()); + + var actions = new Action[] + { + () => Ensure.Any.IsNotNull(value, ParamName, options), + () => EnsureArg.IsNotNull(value, ParamName, options), + () => Ensure.That(value, ParamName, options).IsNotNull() + }.ToList(); + + actions.ForEach(a => a.Should().Throw()); + } + + [Fact] + public void WhenWithMessageAndCustomExceptionAreSpecified_ThrowsTheCustomExceptionFromTheFactory() + { + object value = null; + OptsFn options = o => o + .WithMessage("Foo bar") + .WithException(new KeyNotFoundException()) + .WithExceptionFactory((_, __) => new InvalidTimeZoneException()); + + var actions = new Action[] + { + () => Ensure.Any.IsNotNull(value, ParamName, options), + () => EnsureArg.IsNotNull(value, ParamName, options), + () => Ensure.That(value, ParamName,options).IsNotNull() + }.ToList(); + + actions.ForEach(a => a.Should().Throw().And.Message.Should().NotContain("Foo Bar")); + } + } + public class WithExceptionTests : UnitTestBase { [Fact] @@ -60,4 +98,4 @@ public void ThrowsExceptionWithTheCustomMessage() } } } -} \ No newline at end of file +} diff --git a/src/tests/UnitTests/UnitTests.csproj b/src/tests/UnitTests/UnitTests.csproj index 7564a555..e96df18d 100644 --- a/src/tests/UnitTests/UnitTests.csproj +++ b/src/tests/UnitTests/UnitTests.csproj @@ -1,7 +1,7 @@  - net452;netcoreapp2.1;netcoreapp3.1;net5.0 + net452;netcoreapp3.1;net5.0 false true