diff --git a/Docs/pages/docs/expectations/13-guid.md b/Docs/pages/docs/expectations/13-guid.md index e4194ced1..e97563b9a 100644 --- a/Docs/pages/docs/expectations/13-guid.md +++ b/Docs/pages/docs/expectations/13-guid.md @@ -17,9 +17,14 @@ await Expect.That(subject).IsNotEqualTo(Guid.Parse("cdd7a485-40a1-4bba-bb8b-d0e9 ## Empty -You can verify that the `Guid` is empty or not: +You can verify that the `Guid` is (null or) empty or not: ```csharp await Expect.That(Guid.Empty).IsEmpty(); await Expect.That(Guid.NewGuid()).IsNotEmpty(); + +Guid? guid1 = Guid.Empty; +await Expect.That(guid1).IsNullOrEmpty(); +Guid? guid2 = Guid.NewGuid(); +await Expect.That(guid2).IsNotNullOrEmpty(); ``` diff --git a/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs b/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs index 5d380bcd1..326a40001 100644 --- a/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs +++ b/Source/aweXpect.SourceGenerators/Helpers/ExpectationToGenerate.cs @@ -40,6 +40,9 @@ public ExpectationToGenerate(string @namespace, case "Remarks": Remarks = namedArgument.Value.Value?.ToString(); break; + case "FailOnNull": + FailOnNull = namedArgument.Value.Value as bool? ?? true; + break; case "Using": Usings = namedArgument.Value.Values.Select(x => x.Value?.ToString()).Where(x => x != null).ToArray()!; @@ -59,6 +62,7 @@ public ExpectationToGenerate(string @namespace, FileName = $"{ClassName}.{Name}.g.cs"; } + public bool FailOnNull { get; } = true; public string[] Usings { get; } = []; public string FileName { get; } public bool IncludeNegated { get; } diff --git a/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs b/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs index 19439fc8a..9ee187ba9 100644 --- a/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs +++ b/Source/aweXpect.SourceGenerators/Helpers/SourceGenerationHelper.cs @@ -81,6 +81,7 @@ public CreateExpectationOnNullableAttribute(string positiveName, string negative OutcomeMethod = outcomeMethod; } + public bool FailOnNull { get; set; } = true; public Type TargetType { get; } public string PositiveName { get; } public string? NegativeName { get; } @@ -133,7 +134,7 @@ public static partial class {{expectationToGenerate.ClassName}} """; } - if (expectationToGenerate.IsNullable) + if (expectationToGenerate.IsNullable && expectationToGenerate.FailOnNull) { result += $$""" private sealed class {{expectationToGenerate.Name}}Constraint(string it, ExpectationGrammars grammars) diff --git a/Source/aweXpect/That/Guids/ThatNullableGuid.IsNullOrEmpty.cs b/Source/aweXpect/That/Guids/ThatNullableGuid.IsNullOrEmpty.cs new file mode 100644 index 000000000..634b2b18a --- /dev/null +++ b/Source/aweXpect/That/Guids/ThatNullableGuid.IsNullOrEmpty.cs @@ -0,0 +1,11 @@ +using System; +using aweXpect.SourceGenerators; + +namespace aweXpect; + +[CreateExpectationOnNullable("Is{Not}NullOrEmpty", "{value} is null || {value} == Guid.Empty", + ExpectationText = "is {not} null or empty", + Using = ["System",], + FailOnNull = false +)] +public static partial class ThatNullableGuid; diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt index 6cccc978e..edb7e5fcf 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt @@ -891,6 +891,8 @@ namespace aweXpect public static aweXpect.Results.AndOrResult> IsEqualTo(this aweXpect.Core.IThat source, System.Guid? expected) { } public static aweXpect.Results.AndOrResult> IsNotEmpty(this aweXpect.Core.IThat source) { } public static aweXpect.Results.AndOrResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.Guid? unexpected) { } + public static aweXpect.Results.AndOrResult> IsNotNullOrEmpty(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNullOrEmpty(this aweXpect.Core.IThat source) { } } public static class ThatNullableTimeOnly { diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt index cdbc04eea..7e1cbf240 100644 --- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt +++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt @@ -630,6 +630,8 @@ namespace aweXpect public static aweXpect.Results.AndOrResult> IsEqualTo(this aweXpect.Core.IThat source, System.Guid? expected) { } public static aweXpect.Results.AndOrResult> IsNotEmpty(this aweXpect.Core.IThat source) { } public static aweXpect.Results.AndOrResult> IsNotEqualTo(this aweXpect.Core.IThat source, System.Guid? unexpected) { } + public static aweXpect.Results.AndOrResult> IsNotNullOrEmpty(this aweXpect.Core.IThat source) { } + public static aweXpect.Results.AndOrResult> IsNullOrEmpty(this aweXpect.Core.IThat source) { } } public static class ThatNullableTimeSpan { diff --git a/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsEmpty.Tests.cs b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsEmpty.Tests.cs index f0ad95b4a..282d362dc 100644 --- a/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsEmpty.Tests.cs +++ b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsEmpty.Tests.cs @@ -11,7 +11,7 @@ public sealed class Tests [Fact] public async Task WhenSubjectIsEmpty_ShouldSucceed() { - Guid subject = Guid.Empty; + Guid? subject = Guid.Empty; async Task Act() => await That(subject).IsEmpty(); diff --git a/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotEmpty.Tests.cs b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotEmpty.Tests.cs index 66f3272ee..4bb2352b8 100644 --- a/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotEmpty.Tests.cs +++ b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotEmpty.Tests.cs @@ -11,7 +11,7 @@ public sealed class Tests [Fact] public async Task WhenSubjectIsEmpty_ShouldFail() { - Guid subject = Guid.Empty; + Guid? subject = Guid.Empty; async Task Act() => await That(subject).IsNotEmpty(); diff --git a/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotNullOrEmpty.Tests.cs b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotNullOrEmpty.Tests.cs new file mode 100644 index 000000000..d8206ead4 --- /dev/null +++ b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNotNullOrEmpty.Tests.cs @@ -0,0 +1,56 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatGuid +{ + public sealed partial class Nullable + { + public sealed class IsNotNullOrEmpty + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsEmpty_ShouldFail() + { + Guid? subject = Guid.Empty; + + async Task Act() + => await That(subject).IsNotNullOrEmpty(); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is not null or empty, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsNotEmpty_ShouldSucceed() + { + Guid? subject = OtherGuid(); + + async Task Act() + => await That(subject).IsNotNullOrEmpty(); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNull_ShouldFail() + { + Guid? subject = null; + + async Task Act() + => await That(subject).IsNotNullOrEmpty(); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is not null or empty, + but it was + """); + } + } + } + } +} diff --git a/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNullOrEmpty.Tests.cs b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNullOrEmpty.Tests.cs new file mode 100644 index 000000000..53a116ccd --- /dev/null +++ b/Tests/aweXpect.Tests/Guids/ThatGuid.Nullable.IsNullOrEmpty.Tests.cs @@ -0,0 +1,51 @@ +namespace aweXpect.Tests; + +public sealed partial class ThatGuid +{ + public sealed partial class Nullable + { + public sealed class IsNullOrEmpty + { + public sealed class Tests + { + [Fact] + public async Task WhenSubjectIsEmpty_ShouldSucceed() + { + Guid? subject = Guid.Empty; + + async Task Act() + => await That(subject).IsNullOrEmpty(); + + await That(Act).DoesNotThrow(); + } + + [Fact] + public async Task WhenSubjectIsNotEmpty_ShouldFail() + { + Guid? subject = OtherGuid(); + + async Task Act() + => await That(subject).IsNullOrEmpty(); + + await That(Act).Throws() + .WithMessage($""" + Expected that subject + is null or empty, + but it was {Formatter.Format(subject)} + """); + } + + [Fact] + public async Task WhenSubjectIsNull_ShouldSucceed() + { + Guid? subject = null; + + async Task Act() + => await That(subject).IsNullOrEmpty(); + + await That(Act).DoesNotThrow(); + } + } + } + } +}