diff --git a/Source/aweXpect.Core/Equivalency/EquivalencyExpectationBuilder.cs b/Source/aweXpect.Core/Equivalency/EquivalencyExpectationBuilder.cs index d9797643c..085cbacca 100644 --- a/Source/aweXpect.Core/Equivalency/EquivalencyExpectationBuilder.cs +++ b/Source/aweXpect.Core/Equivalency/EquivalencyExpectationBuilder.cs @@ -46,8 +46,18 @@ public override async Task IsMetBy( else if (value is null) { T? typedDefault = default; - _result = new NotMatchingTypesResult(typedDefault, - await GetRootNode().IsMetBy(typedDefault, context, cancellationToken)); + if (typedDefault is not null) + { + _result = new NotMatchingTypesResult(typedDefault, null); + } + else + { + // ReSharper disable ExpressionIsAlwaysNull + // typedDefault is used to have the correct generic overload in `IsMetBy`. + _result = new NotMatchingTypesResult(typedDefault, + await GetRootNode().IsMetBy(typedDefault, context, cancellationToken)); + // ReSharper restore ExpressionIsAlwaysNull + } } else { @@ -66,7 +76,7 @@ public NotMatchingTypesResult(object? value, ConstraintResult? inner) : base(Fur { _value = value; _inner = inner; - Outcome = Outcome.Failure; + Outcome = inner?.Outcome ?? Outcome.Failure; } public override void AppendExpectation(StringBuilder stringBuilder, string? indentation = null) diff --git a/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.ItIs.Tests.cs b/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.ItIs.Tests.cs index 08d8a16a1..ad33c9097 100644 --- a/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.ItIs.Tests.cs +++ b/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.ItIs.Tests.cs @@ -101,6 +101,42 @@ Property IntValue was 42 """); } + /// + /// It is not possible to determine the type of ! + /// + [Fact] + public async Task WhenAnyNotNullCheckAndItIsNull_ShouldFail() + { + DummyClass subject = new() + { + StringValue = null, + NullableIntValue = null, + IntValue = 42, + }; + var expected = new + { + StringValue = It.Is().That.IsEmpty(), + NullableIntValue = It.Is().That.IsEqualTo(0), + IntValue = It.Is().That.IsGreaterThan(2), + }; + + async Task Act() + => await That(subject).IsEquivalentTo(expected); + + await That(Act).Throws() + .WithMessage(""" + Expected that subject + is equivalent to { StringValue = is string that is empty, NullableIntValue = is int? that is equal to 0, IntValue = is int that is greater than 2 }, + but it was not: + Property StringValue was + and + Property NullableIntValue was + + Equivalency options: + - include public fields and properties + """); + } + [Fact] public async Task WhenAnyTypeDoesNotMatch_ShouldFail() { @@ -164,8 +200,8 @@ Property StringValue was "" /// /// It is not possible to determine the type of ! /// - [Fact] - public async Task WhenCheckForNullAndItIsNull_ShouldStillFail() + [Fact(Skip="TODO: Re-Enable after the next Core update")] + public async Task WhenCheckForNullAndItIsNull_ShouldSucceed() { DummyClass subject = new() { @@ -181,27 +217,19 @@ public async Task WhenCheckForNullAndItIsNull_ShouldStillFail() async Task Act() => await That(subject).IsEquivalentTo(expected); - await That(Act).Throws() - .WithMessage(""" - Expected that subject - is equivalent to { StringValue = is string that is null, IntValue = is int that is greater than 2 }, - but it was not: - Property StringValue was - - Equivalency options: - - include public fields and properties - """); + await That(Act).DoesNotThrow(); } - } - // ReSharper disable UnusedAutoPropertyAccessor.Local - private sealed class DummyClass - { - public string? StringValue { get; set; } - public int IntValue { get; set; } - public bool BoolValue { get; set; } + // ReSharper disable UnusedAutoPropertyAccessor.Local + private sealed class DummyClass + { + public string? StringValue { get; set; } + public int? NullableIntValue { get; set; } + public int IntValue { get; set; } + public bool BoolValue { get; set; } + } + // ReSharper restore UnusedAutoPropertyAccessor.Local } - // ReSharper restore UnusedAutoPropertyAccessor.Local } } }