diff --git a/src/projects/EnsureThat/Internals/ExceptionFactory.cs b/src/projects/EnsureThat/IExceptionFactory.cs similarity index 72% rename from src/projects/EnsureThat/Internals/ExceptionFactory.cs rename to src/projects/EnsureThat/IExceptionFactory.cs index 8005370..c08150c 100644 --- a/src/projects/EnsureThat/Internals/ExceptionFactory.cs +++ b/src/projects/EnsureThat/IExceptionFactory.cs @@ -1,11 +1,17 @@ using System; using JetBrains.Annotations; - using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; -namespace EnsureThat.Internals +namespace EnsureThat { - internal sealed class ExceptionFactory : IExceptionFactory + 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); + } + + public sealed class ExceptionFactory : IExceptionFactory { [return: NotNull] [Pure] @@ -15,6 +21,9 @@ public Exception ArgumentException(string defaultMessage, string paramName, Opts { var opts = optsFn(new EnsureOptions()); + if (opts.CustomExceptionFactory != null) + return opts.CustomExceptionFactory(defaultMessage, paramName); + if (opts.CustomException != null) return opts.CustomException; @@ -54,6 +63,9 @@ public Exception ArgumentOutOfRangeException(string defaultMessage, stri { var opts = optsFn(new EnsureOptions()); + if (opts.CustomExceptionFactory != null) + return opts.CustomExceptionFactory(defaultMessage, paramName); + if (opts.CustomException != null) return opts.CustomException; diff --git a/src/projects/EnsureThat/Internals/IExceptionFactory.cs b/src/projects/EnsureThat/Internals/IExceptionFactory.cs deleted file mode 100644 index ce41ba3..0000000 --- a/src/projects/EnsureThat/Internals/IExceptionFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -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/ExceptionFactoryTests.cs b/src/tests/UnitTests/ExceptionFactoryTests.cs new file mode 100644 index 0000000..236d358 --- /dev/null +++ b/src/tests/UnitTests/ExceptionFactoryTests.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using EnsureThat; +using FluentAssertions; +using Xunit; + +namespace UnitTests +{ + public class ExceptionFactoryTests : UnitTestBase + { + private const string DefaultMessage = "54a170d6997c400487c177768f72aa7a"; + private const string CustomMessage = "3b3e6082d4c1482fbc775abc11a2e415"; + private const string DummyValue = "61927e885db34e08b9a7211c7eb74c36"; + private readonly Exception _exceptionFactoryException = new KeyNotFoundException(); + private readonly Exception _customException = new("036859e6693848b199575feffe17bd46"); + private readonly ExceptionFactory _sut = new(); + + [Fact] + public void ArgumentException_UsesDefaults_WhenNoOptionsAreProvided() + => _sut.ArgumentException( + DefaultMessage, + ParamName) + .Should() + .BeEquivalentTo(new ArgumentException(DefaultMessage, ParamName)); + + [Fact] + public void ArgumentException_UsesExceptionFactory_WhenOptionsAlsoContainsCustomExceptionAndCustomMessage() + => _sut.ArgumentException( + DefaultMessage, + ParamName, + o => o + .WithMessage(CustomMessage) + .WithException(_customException) + .WithExceptionFactory((_, __) => _exceptionFactoryException)) + .Should() + .Be(_exceptionFactoryException); + + [Fact] + public void ArgumentException_UsesCustomException_WhenOptionsAlsoContainsCustomMessage() + => _sut.ArgumentException( + DefaultMessage, + ParamName, + o => o + .WithMessage(CustomMessage) + .WithException(_customException)) + .Should() + .Be(_customException); + + [Fact] + public void ArgumentException_UsesCustomMessage_WhenOptionsHasNoOtherConfig() + => _sut.ArgumentException( + DefaultMessage, + ParamName, + o => o + .WithMessage(CustomMessage)) + .Should() + .BeEquivalentTo(new ArgumentException(CustomMessage, ParamName)); + + [Fact] + public void ArgumentNullException_UsesDefaults_WhenNoOptionsAreProvided() + => _sut.ArgumentNullException( + DefaultMessage, + ParamName) + .Should() + .BeEquivalentTo(new ArgumentNullException(ParamName, DefaultMessage)); + + [Fact] + public void ArgumentNullException_UsesExceptionFactory_WhenOptionsAlsoContainsCustomExceptionAndCustomMessage() + => _sut.ArgumentNullException( + DefaultMessage, + ParamName, + o => o + .WithMessage(CustomMessage) + .WithException(_customException) + .WithExceptionFactory((_, __) => _exceptionFactoryException)) + .Should() + .Be(_exceptionFactoryException); + + [Fact] + public void ArgumentNullException_UsesCustomException_WhenOptionsAlsoContainsCustomMessage() + => _sut.ArgumentNullException( + DefaultMessage, + ParamName, + o => o + .WithMessage(CustomMessage) + .WithException(_customException)) + .Should() + .Be(_customException); + + [Fact] + public void ArgumentNullException_UsesCustomMessage_WhenOptionsHasNoOtherConfig() + => _sut.ArgumentNullException( + DefaultMessage, + ParamName, + o => o + .WithMessage(CustomMessage)) + .Should() + .BeEquivalentTo(new ArgumentNullException(ParamName, CustomMessage)); + + [Fact] + public void ArgumentOutOfRangeException_UsesDefaults_WhenNoOptionsAreProvided() + => _sut.ArgumentOutOfRangeException( + DefaultMessage, + ParamName, + DummyValue) + .Should() + .BeEquivalentTo(new ArgumentOutOfRangeException(ParamName, DummyValue, DefaultMessage)); + + [Fact] + public void ArgumentOutOfRangeException_UsesExceptionFactory_WhenOptionsAlsoContainsCustomExceptionAndCustomMessage() + => _sut.ArgumentOutOfRangeException( + DefaultMessage, + ParamName, + DummyValue, + o => o + .WithMessage(CustomMessage) + .WithException(_customException) + .WithExceptionFactory((_, __) => _exceptionFactoryException)) + .Should() + .Be(_exceptionFactoryException); + + [Fact] + public void ArgumentOutOfRangeException_UsesCustomException_WhenOptionsAlsoContainsCustomMessage() + => _sut.ArgumentOutOfRangeException( + DefaultMessage, + ParamName, + DummyValue, + o => o + .WithMessage(CustomMessage) + .WithException(_customException)) + .Should() + .Be(_customException); + + [Fact] + public void ArgumentOutOfRangeException_UsesCustomMessage_WhenOptionsHasNoOtherConfig() + => _sut.ArgumentOutOfRangeException( + DefaultMessage, + ParamName, + DummyValue, + o => o + .WithMessage(CustomMessage)) + .Should() + .BeEquivalentTo(new ArgumentOutOfRangeException(ParamName, DummyValue, CustomMessage)); + } +}