From bcdaff234574593eb9619e91337871f7ab7b05db Mon Sep 17 00:00:00 2001 From: Dennis Doomen Date: Thu, 28 Nov 2024 19:39:12 +0100 Subject: [PATCH] SAVEPOINT --- Src/FluentAssertions/Common/MemberPath.cs | 2 +- Src/FluentAssertions/Equivalency/Field.cs | 1 + Src/FluentAssertions/Equivalency/INode.cs | 20 +++++++ Src/FluentAssertions/Equivalency/Node.cs | 19 +++++++ Src/FluentAssertions/Equivalency/Property.cs | 1 + .../StructuralEqualityEquivalencyStep.cs | 1 + .../MemberMatchingSpecs.cs | 53 ++++++++++++++++++- 7 files changed, 95 insertions(+), 2 deletions(-) diff --git a/Src/FluentAssertions/Common/MemberPath.cs b/Src/FluentAssertions/Common/MemberPath.cs index 77cf3a3c20..c16f35d6a1 100644 --- a/Src/FluentAssertions/Common/MemberPath.cs +++ b/Src/FluentAssertions/Common/MemberPath.cs @@ -20,7 +20,7 @@ internal class MemberPath private static readonly MemberPathSegmentEqualityComparer MemberPathSegmentEqualityComparer = new(); public MemberPath(IMember member, string parentPath) - : this(member.ReflectedType, member.DeclaringType, parentPath.Combine(member.Name)) + : this(member.ReflectedType, member.DeclaringType, parentPath.Combine(member.OriginalName ?? member.Name)) { } diff --git a/Src/FluentAssertions/Equivalency/Field.cs b/Src/FluentAssertions/Equivalency/Field.cs index e44a523d20..6eada98b93 100644 --- a/Src/FluentAssertions/Equivalency/Field.cs +++ b/Src/FluentAssertions/Equivalency/Field.cs @@ -24,6 +24,7 @@ public Field(Type reflectedType, FieldInfo fieldInfo, INode parent) DeclaringType = fieldInfo.DeclaringType; ReflectedType = reflectedType; Path = parent.PathAndName; + OriginalPath = parent.OriginalPathAndName; GetSubjectId = parent.GetSubjectId; Name = fieldInfo.Name; Type = fieldInfo.FieldType; diff --git a/Src/FluentAssertions/Equivalency/INode.cs b/Src/FluentAssertions/Equivalency/INode.cs index 29274315a0..0388d2d52b 100644 --- a/Src/FluentAssertions/Equivalency/INode.cs +++ b/Src/FluentAssertions/Equivalency/INode.cs @@ -79,4 +79,24 @@ public interface INode /// Gets a value indicating if the root of this graph is a collection. /// bool RootIsCollection { get; } + + [CanBeNull] + string OriginalPath { get; set; } + + /// + /// Gets the original name of this node before was updated, for example + /// when the expectation is mapped to a different member of the subject. + /// + [CanBeNull] + string OriginalName { get; set; } + + /// + /// Gets the full path from the root object up to and including the name of the node as it was originally defined, for example + /// when the expectation is mapped to a different member of the subject. + /// + /// + /// "Parent[0]" + /// + [CanBeNull] + string OriginalPathAndName { get; } } diff --git a/Src/FluentAssertions/Equivalency/Node.cs b/Src/FluentAssertions/Equivalency/Node.cs index 145d8291f7..96daef5b05 100644 --- a/Src/FluentAssertions/Equivalency/Node.cs +++ b/Src/FluentAssertions/Equivalency/Node.cs @@ -93,6 +93,7 @@ public static INode FromCollectionItem(string index, INode parent) ParentType = parent.Type, Name = "[" + index + "]", Path = parent.PathAndName, + OriginalPath = parent.OriginalPath, GetSubjectId = parent.GetSubjectId, RootIsCollection = parent.RootIsCollection }; @@ -106,11 +107,29 @@ public static INode FromDictionaryItem(object key, INode parent) ParentType = parent.Type, Name = "[" + key + "]", Path = parent.PathAndName, + OriginalPath = parent.OriginalPath, GetSubjectId = parent.GetSubjectId, RootIsCollection = parent.RootIsCollection }; } + public string OriginalPath { get; set; } + + public string OriginalName { get; set; } + + public string OriginalPathAndName + { + get + { + if (OriginalPath is not null || OriginalName is not null) + { + return (OriginalPath ?? string.Empty).Combine(OriginalName ?? Name); + } + + return null; + } + } + public override bool Equals(object obj) { if (obj is null) diff --git a/Src/FluentAssertions/Equivalency/Property.cs b/Src/FluentAssertions/Equivalency/Property.cs index fe43d16e1a..f8d29eba4a 100644 --- a/Src/FluentAssertions/Equivalency/Property.cs +++ b/Src/FluentAssertions/Equivalency/Property.cs @@ -28,6 +28,7 @@ public Property(Type reflectedType, PropertyInfo propertyInfo, INode parent) Type = propertyInfo.PropertyType; ParentType = propertyInfo.DeclaringType; Path = parent.PathAndName; + OriginalPath = parent.OriginalPathAndName; GetSubjectId = parent.GetSubjectId; RootIsCollection = parent.RootIsCollection; } diff --git a/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs b/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs index 4b8091cf94..5dc41e120b 100644 --- a/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs +++ b/Src/FluentAssertions/Equivalency/Steps/StructuralEqualityEquivalencyStep.cs @@ -70,6 +70,7 @@ private static void AssertMemberEquality(Comparands comparands, IEquivalencyVali { // In case the matching process selected a different member on the subject, // adjust the current member so that assertion failures report the proper name. + selectedMember.OriginalName = selectedMember.Name; selectedMember.Name = matchingMember.Name; } diff --git a/Tests/FluentAssertions.Equivalency.Specs/MemberMatchingSpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/MemberMatchingSpecs.cs index 231c68de0a..84f1a9ff43 100644 --- a/Tests/FluentAssertions.Equivalency.Specs/MemberMatchingSpecs.cs +++ b/Tests/FluentAssertions.Equivalency.Specs/MemberMatchingSpecs.cs @@ -87,7 +87,8 @@ public void Nested_properties_can_be_mapped_using_a_nested_type_and_property_nam public void Nested_explicitly_implemented_properties_can_be_mapped_using_a_nested_type_and_property_names() { // Arrange - var subject = new ParentOfSubjectWithExplicitlyImplementedProperty(new[] { new SubjectWithExplicitImplementedProperty() }); + var subject = + new ParentOfSubjectWithExplicitlyImplementedProperty(new[] { new SubjectWithExplicitImplementedProperty() }); ((IProperty)subject.Children[0]).Property = "Hello"; @@ -135,6 +136,56 @@ public void Nested_properties_can_be_mapped_using_a_nested_type_and_a_property_e e => e.Property2, s => s.Property1)); } + private class Customer + { + public Residence Address { get; set; } + + public class Residence + { + public string Address { get; set; } + } + } + + private class CustomerDto + { + public ResidenceDto AddressInformation { get; set; } + + public class ResidenceDto + { + public string Address { get; set; } + + public bool IsValidated { get; set; } + } + } + + [Fact] + public void CustomerDto_To_Customer_ShouldBeEquivalent() + { + //Arrange + var expectation = new CustomerDto + { + AddressInformation = new CustomerDto.ResidenceDto + { + Address = "123 Main St", + IsValidated = true, + }, + }; + + //Act + var subject = new Customer + { + Address = new Customer.Residence + { + Address = "123 Main St", + }, + }; + + //Assert + subject.Should().BeEquivalentTo(expectation, o => o + .Excluding(r => r.AddressInformation.IsValidated) + .WithMapping(s => s.AddressInformation, d => d.Address)); + } + [Fact] public void Nested_properties_on_a_collection_can_be_mapped_using_a_dotted_path() {