diff --git a/Src/FluentAssertions/Equivalency/Execution/ObjectInfo.cs b/Src/FluentAssertions/Equivalency/Execution/ObjectInfo.cs
index 62c233fd28..0f45024f21 100644
--- a/Src/FluentAssertions/Equivalency/Execution/ObjectInfo.cs
+++ b/Src/FluentAssertions/Equivalency/Execution/ObjectInfo.cs
@@ -7,6 +7,7 @@ internal class ObjectInfo : IObjectInfo
public ObjectInfo(Comparands comparands, INode currentNode)
{
Type = currentNode.Type;
+ ParentType = currentNode.ParentType;
Path = currentNode.PathAndName;
CompileTimeType = comparands.CompileTimeType;
RuntimeType = comparands.RuntimeType;
@@ -14,6 +15,8 @@ public ObjectInfo(Comparands comparands, INode currentNode)
public Type Type { get; }
+ public Type ParentType { get; }
+
public string Path { get; set; }
public Type CompileTimeType { get; }
diff --git a/Src/FluentAssertions/Equivalency/Field.cs b/Src/FluentAssertions/Equivalency/Field.cs
index 4e6124aee8..d82f1e46bc 100644
--- a/Src/FluentAssertions/Equivalency/Field.cs
+++ b/Src/FluentAssertions/Equivalency/Field.cs
@@ -27,6 +27,7 @@ public Field(Type reflectedType, FieldInfo fieldInfo, INode parent)
GetSubjectId = parent.GetSubjectId;
Name = fieldInfo.Name;
Type = fieldInfo.FieldType;
+ ParentType = fieldInfo.DeclaringType;
RootIsCollection = parent.RootIsCollection;
}
diff --git a/Src/FluentAssertions/Equivalency/INode.cs b/Src/FluentAssertions/Equivalency/INode.cs
index f95657c1b5..4e2abbd478 100644
--- a/Src/FluentAssertions/Equivalency/INode.cs
+++ b/Src/FluentAssertions/Equivalency/INode.cs
@@ -1,9 +1,11 @@
using System;
+using JetBrains.Annotations;
namespace FluentAssertions.Equivalency;
///
-/// Represents a node in the object graph as it is expected in a structural equivalency check.
+/// Represents a node in the object graph that is being compared as part of a structural equivalency check.
+/// This can be the root object, a collection item, a dictionary element, a property or a field.
///
public interface INode
{
@@ -22,10 +24,19 @@ public interface INode
string Name { get; set; }
///
- /// Gets the type of this node.
+ /// Gets the type of this node, e.g. the type of the field or property, or the type of the collection item.
///
Type Type { get; }
+ ///
+ /// Gets the type of the parent node, e.g. the type that declares a property or field.
+ ///
+ ///
+ /// Is null for the root object.
+ ///
+ [CanBeNull]
+ Type ParentType { get; }
+
///
/// Gets the path from the root object UNTIL the current node, separated by dots or index/key brackets.
///
diff --git a/Src/FluentAssertions/Equivalency/IObjectInfo.cs b/Src/FluentAssertions/Equivalency/IObjectInfo.cs
index 615cd119b9..1b494a0416 100644
--- a/Src/FluentAssertions/Equivalency/IObjectInfo.cs
+++ b/Src/FluentAssertions/Equivalency/IObjectInfo.cs
@@ -1,4 +1,5 @@
using System;
+using JetBrains.Annotations;
namespace FluentAssertions.Equivalency;
@@ -8,10 +9,20 @@ namespace FluentAssertions.Equivalency;
public interface IObjectInfo
{
///
- /// Gets the type of this node.
+ /// Gets the type of the object
///
+ [Obsolete("Use CompileTimeType or RuntimeType instead")]
Type Type { get; }
+ ///
+ /// Gets the type of the parent, e.g. the type that declares a property or field.
+ ///
+ ///
+ /// Is null for the root object.
+ ///
+ [CanBeNull]
+ Type ParentType { get; }
+
///
/// Gets the full path from the root object until the current node separated by dots.
///
diff --git a/Src/FluentAssertions/Equivalency/Node.cs b/Src/FluentAssertions/Equivalency/Node.cs
index 71315cfd32..90d94a3042 100644
--- a/Src/FluentAssertions/Equivalency/Node.cs
+++ b/Src/FluentAssertions/Equivalency/Node.cs
@@ -1,15 +1,11 @@
using System;
using System.Collections;
-using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using FluentAssertions.Common;
namespace FluentAssertions.Equivalency;
-///
-/// Represents a node in the object graph that is being compared as part of a structural equivalency check.
-///
public class Node : INode
{
private static readonly Regex MatchFirstIndex = new(@"^\[\d+\]$");
@@ -22,6 +18,8 @@ public class Node : INode
public Type Type { get; protected set; }
+ public Type ParentType { get; protected set; }
+
public string Path
{
get => path;
@@ -82,6 +80,7 @@ public static INode From(GetSubjectId getSubjectId)
Name = string.Empty,
Path = string.Empty,
Type = typeof(T),
+ ParentType = null,
RootIsCollection = IsCollection(typeof(T))
};
}
@@ -91,6 +90,7 @@ public static INode FromCollectionItem(string index, INode parent)
return new Node
{
Type = typeof(T),
+ ParentType = parent.Type,
Name = "[" + index + "]",
Path = parent.PathAndName,
GetSubjectId = parent.GetSubjectId,
@@ -103,6 +103,7 @@ public static INode FromDictionaryItem(object key, INode parent)
return new Node
{
Type = typeof(T),
+ ParentType = parent.Type,
Name = "[" + key + "]",
Path = parent.PathAndName,
GetSubjectId = parent.GetSubjectId,
diff --git a/Src/FluentAssertions/Equivalency/Ordering/CollectionMemberObjectInfo.cs b/Src/FluentAssertions/Equivalency/Ordering/CollectionMemberObjectInfo.cs
index ac2898ddd3..5f5b9d63c3 100644
--- a/Src/FluentAssertions/Equivalency/Ordering/CollectionMemberObjectInfo.cs
+++ b/Src/FluentAssertions/Equivalency/Ordering/CollectionMemberObjectInfo.cs
@@ -7,7 +7,12 @@ internal class CollectionMemberObjectInfo : IObjectInfo
public CollectionMemberObjectInfo(IObjectInfo context)
{
Path = GetAdjustedPropertyPath(context.Path);
+
+#pragma warning disable CS0618
Type = context.Type;
+#pragma warning restore CS0618
+
+ ParentType = context.ParentType;
RuntimeType = context.RuntimeType;
CompileTimeType = context.CompileTimeType;
}
@@ -18,6 +23,8 @@ private static string GetAdjustedPropertyPath(string propertyPath)
}
public Type Type { get; }
+
+ public Type ParentType { get; }
public string Path { get; set; }
diff --git a/Src/FluentAssertions/Equivalency/Property.cs b/Src/FluentAssertions/Equivalency/Property.cs
index 93e2429ccc..4ef87c21b9 100644
--- a/Src/FluentAssertions/Equivalency/Property.cs
+++ b/Src/FluentAssertions/Equivalency/Property.cs
@@ -26,6 +26,7 @@ public Property(Type reflectedType, PropertyInfo propertyInfo, INode parent)
DeclaringType = propertyInfo.DeclaringType;
Name = propertyInfo.Name;
Type = propertyInfo.PropertyType;
+ ParentType = propertyInfo.DeclaringType;
Path = parent.PathAndName;
GetSubjectId = parent.GetSubjectId;
RootIsCollection = parent.RootIsCollection;
@@ -36,7 +37,7 @@ public object GetValue(object obj)
return propertyInfo.GetValue(obj);
}
- public Type DeclaringType { get; private set; }
+ public Type DeclaringType { get; }
public Type ReflectedType { get; }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
index 68db356d87..f2d2ba526e 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net47.verified.txt
@@ -935,6 +935,7 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; }
bool IsRoot { get; }
string Name { get; set; }
+ System.Type ParentType { get; }
string Path { get; }
string PathAndName { get; }
bool RootIsCollection { get; }
@@ -943,8 +944,10 @@ namespace FluentAssertions.Equivalency
public interface IObjectInfo
{
System.Type CompileTimeType { get; }
+ System.Type ParentType { get; }
string Path { get; set; }
System.Type RuntimeType { get; }
+ [System.Obsolete("Use CompileTimeType or RuntimeType instead")]
System.Type Type { get; }
}
public interface IOrderingRule
@@ -982,6 +985,7 @@ namespace FluentAssertions.Equivalency
public FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; set; }
public bool IsRoot { get; }
public string Name { get; set; }
+ public System.Type ParentType { get; set; }
public string Path { get; set; }
public string PathAndName { get; }
public bool RootIsCollection { get; set; }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
index be8713a032..6f8abf4512 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/net6.0.verified.txt
@@ -947,6 +947,7 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; }
bool IsRoot { get; }
string Name { get; set; }
+ System.Type ParentType { get; }
string Path { get; }
string PathAndName { get; }
bool RootIsCollection { get; }
@@ -955,8 +956,10 @@ namespace FluentAssertions.Equivalency
public interface IObjectInfo
{
System.Type CompileTimeType { get; }
+ System.Type ParentType { get; }
string Path { get; set; }
System.Type RuntimeType { get; }
+ [System.Obsolete("Use CompileTimeType or RuntimeType instead")]
System.Type Type { get; }
}
public interface IOrderingRule
@@ -994,6 +997,7 @@ namespace FluentAssertions.Equivalency
public FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; set; }
public bool IsRoot { get; }
public string Name { get; set; }
+ public System.Type ParentType { get; set; }
public string Path { get; set; }
public string PathAndName { get; }
public bool RootIsCollection { get; set; }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt
index da807b1d68..9e06dba6fd 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp2.1.verified.txt
@@ -935,6 +935,7 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; }
bool IsRoot { get; }
string Name { get; set; }
+ System.Type ParentType { get; }
string Path { get; }
string PathAndName { get; }
bool RootIsCollection { get; }
@@ -943,8 +944,10 @@ namespace FluentAssertions.Equivalency
public interface IObjectInfo
{
System.Type CompileTimeType { get; }
+ System.Type ParentType { get; }
string Path { get; set; }
System.Type RuntimeType { get; }
+ [System.Obsolete("Use CompileTimeType or RuntimeType instead")]
System.Type Type { get; }
}
public interface IOrderingRule
@@ -982,6 +985,7 @@ namespace FluentAssertions.Equivalency
public FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; set; }
public bool IsRoot { get; }
public string Name { get; set; }
+ public System.Type ParentType { get; set; }
public string Path { get; set; }
public string PathAndName { get; }
public bool RootIsCollection { get; set; }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt
index 0e54c1699a..26f5978a06 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netcoreapp3.0.verified.txt
@@ -935,6 +935,7 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; }
bool IsRoot { get; }
string Name { get; set; }
+ System.Type ParentType { get; }
string Path { get; }
string PathAndName { get; }
bool RootIsCollection { get; }
@@ -943,8 +944,10 @@ namespace FluentAssertions.Equivalency
public interface IObjectInfo
{
System.Type CompileTimeType { get; }
+ System.Type ParentType { get; }
string Path { get; set; }
System.Type RuntimeType { get; }
+ [System.Obsolete("Use CompileTimeType or RuntimeType instead")]
System.Type Type { get; }
}
public interface IOrderingRule
@@ -982,6 +985,7 @@ namespace FluentAssertions.Equivalency
public FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; set; }
public bool IsRoot { get; }
public string Name { get; set; }
+ public System.Type ParentType { get; set; }
public string Path { get; set; }
public string PathAndName { get; }
public bool RootIsCollection { get; set; }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt
index eeaa8fe8dc..d64dbc6ef5 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.0.verified.txt
@@ -928,6 +928,7 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; }
bool IsRoot { get; }
string Name { get; set; }
+ System.Type ParentType { get; }
string Path { get; }
string PathAndName { get; }
bool RootIsCollection { get; }
@@ -936,8 +937,10 @@ namespace FluentAssertions.Equivalency
public interface IObjectInfo
{
System.Type CompileTimeType { get; }
+ System.Type ParentType { get; }
string Path { get; set; }
System.Type RuntimeType { get; }
+ [System.Obsolete("Use CompileTimeType or RuntimeType instead")]
System.Type Type { get; }
}
public interface IOrderingRule
@@ -975,6 +978,7 @@ namespace FluentAssertions.Equivalency
public FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; set; }
public bool IsRoot { get; }
public string Name { get; set; }
+ public System.Type ParentType { get; set; }
public string Path { get; set; }
public string PathAndName { get; }
public bool RootIsCollection { get; set; }
diff --git a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt
index 1e4f06c121..fdddf3faf8 100644
--- a/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt
+++ b/Tests/Approval.Tests/ApprovedApi/FluentAssertions/netstandard2.1.verified.txt
@@ -935,6 +935,7 @@ namespace FluentAssertions.Equivalency
FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; }
bool IsRoot { get; }
string Name { get; set; }
+ System.Type ParentType { get; }
string Path { get; }
string PathAndName { get; }
bool RootIsCollection { get; }
@@ -943,8 +944,10 @@ namespace FluentAssertions.Equivalency
public interface IObjectInfo
{
System.Type CompileTimeType { get; }
+ System.Type ParentType { get; }
string Path { get; set; }
System.Type RuntimeType { get; }
+ [System.Obsolete("Use CompileTimeType or RuntimeType instead")]
System.Type Type { get; }
}
public interface IOrderingRule
@@ -982,6 +985,7 @@ namespace FluentAssertions.Equivalency
public FluentAssertions.Equivalency.GetSubjectId GetSubjectId { get; set; }
public bool IsRoot { get; }
public string Name { get; set; }
+ public System.Type ParentType { get; set; }
public string Path { get; set; }
public string PathAndName { get; }
public bool RootIsCollection { get; set; }
diff --git a/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs b/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs
index ff66adec94..f6dfce3a8b 100644
--- a/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs
+++ b/Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs
@@ -226,6 +226,27 @@ public void When_property_of_other_is_incompatible_with_generic_type_the_message
.WithMessage("*Id*from expectation*System.String*System.Double*");
}
+ [Fact]
+ public void Can_exclude_all_properties_of_the_parent_type()
+ {
+ // Arrange
+ var subject = new
+ {
+ Id = "foo",
+ };
+
+ var expectation = new
+ {
+ Id = "bar",
+ };
+
+ // Act
+ subject.Should().BeEquivalentTo(expectation,
+ o => o
+ .Using(c => c.Subject.Should().HaveLength(c.Expectation.Length))
+ .When(si => si.ParentType == expectation.GetType() && si.Path.EndsWith("Id", StringComparison.InvariantCulture)));
+ }
+
[Fact]
public void When_property_of_subject_is_incompatible_with_generic_type_the_message_should_include_generic_type()
{
diff --git a/docs/_pages/releases.md b/docs/_pages/releases.md
index c81b898eaa..ffe878de27 100644
--- a/docs/_pages/releases.md
+++ b/docs/_pages/releases.md
@@ -12,6 +12,7 @@ sidebar:
### What's new
* Added `ContainInConsecutiveOrder` and `NotContainInConsecutiveOrder` assertions to check if a collection contains items in a specific order and to be consecutive - [#1963](https://github.com/fluentassertions/fluentassertions/pull/1963)
* Added `NotCompleteWithinAsync` for assertions on `Task` - [#1967](https://github.com/fluentassertions/fluentassertions/pull/1967)
+* Added a `ParentType` to `IObjectInfo` to help determining the parent in a call to `Using`/`When` constructs - [#1950](https://github.com/fluentassertions/fluentassertions/pull/1950)
### Fixes
* Fix `For`/`Exclude` not excluding properties in objects in a collection - [#1953](https://github.com/fluentassertions/fluentassertions/pull/1953)