Skip to content

Commit

Permalink
Adds IObjectInfo.ParentType to help determining the declaring type in…
Browse files Browse the repository at this point in the history
… a call to Using/When constructs - (fluentassertions#1950)
  • Loading branch information
dennisdoomen authored and jnyrup committed Oct 22, 2022
1 parent 41e0c2a commit 9fcaa69
Show file tree
Hide file tree
Showing 15 changed files with 89 additions and 8 deletions.
3 changes: 3 additions & 0 deletions Src/FluentAssertions/Equivalency/Execution/ObjectInfo.cs
Expand Up @@ -7,13 +7,16 @@ 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;
}

public Type Type { get; }

public Type ParentType { get; }

public string Path { get; set; }

public Type CompileTimeType { get; }
Expand Down
1 change: 1 addition & 0 deletions Src/FluentAssertions/Equivalency/Field.cs
Expand Up @@ -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;
}

Expand Down
15 changes: 13 additions & 2 deletions Src/FluentAssertions/Equivalency/INode.cs
@@ -1,9 +1,11 @@
using System;
using JetBrains.Annotations;

namespace FluentAssertions.Equivalency;

/// <summary>
/// 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.
/// </summary>
public interface INode
{
Expand All @@ -22,10 +24,19 @@ public interface INode
string Name { get; set; }

/// <summary>
/// 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.
/// </summary>
Type Type { get; }

/// <summary>
/// Gets the type of the parent node, e.g. the type that declares a property or field.
/// </summary>
/// <value>
/// Is <c>null</c> for the root object.
/// </value>
[CanBeNull]
Type ParentType { get; }

/// <summary>
/// Gets the path from the root object UNTIL the current node, separated by dots or index/key brackets.
/// </summary>
Expand Down
13 changes: 12 additions & 1 deletion Src/FluentAssertions/Equivalency/IObjectInfo.cs
@@ -1,4 +1,5 @@
using System;
using JetBrains.Annotations;

namespace FluentAssertions.Equivalency;

Expand All @@ -8,10 +9,20 @@ namespace FluentAssertions.Equivalency;
public interface IObjectInfo
{
/// <summary>
/// Gets the type of this node.
/// Gets the type of the object
/// </summary>
[Obsolete("Use CompileTimeType or RuntimeType instead")]
Type Type { get; }

/// <summary>
/// Gets the type of the parent, e.g. the type that declares a property or field.
/// </summary>
/// <value>
/// Is <c>null</c> for the root object.
/// </value>
[CanBeNull]
Type ParentType { get; }

/// <summary>
/// Gets the full path from the root object until the current node separated by dots.
/// </summary>
Expand Down
9 changes: 5 additions & 4 deletions 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;

/// <summary>
/// Represents a node in the object graph that is being compared as part of a structural equivalency check.
/// </summary>
public class Node : INode
{
private static readonly Regex MatchFirstIndex = new(@"^\[\d+\]$");
Expand All @@ -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;
Expand Down Expand Up @@ -82,6 +80,7 @@ public static INode From<T>(GetSubjectId getSubjectId)
Name = string.Empty,
Path = string.Empty,
Type = typeof(T),
ParentType = null,
RootIsCollection = IsCollection(typeof(T))
};
}
Expand All @@ -91,6 +90,7 @@ public static INode FromCollectionItem<T>(string index, INode parent)
return new Node
{
Type = typeof(T),
ParentType = parent.Type,
Name = "[" + index + "]",
Path = parent.PathAndName,
GetSubjectId = parent.GetSubjectId,
Expand All @@ -103,6 +103,7 @@ public static INode FromDictionaryItem<T>(object key, INode parent)
return new Node
{
Type = typeof(T),
ParentType = parent.Type,
Name = "[" + key + "]",
Path = parent.PathAndName,
GetSubjectId = parent.GetSubjectId,
Expand Down
Expand Up @@ -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;
}
Expand All @@ -18,6 +23,8 @@ private static string GetAdjustedPropertyPath(string propertyPath)
}

public Type Type { get; }

public Type ParentType { get; }

public string Path { get; set; }

Expand Down
3 changes: 2 additions & 1 deletion Src/FluentAssertions/Equivalency/Property.cs
Expand Up @@ -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;
Expand All @@ -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; }

Expand Down
Expand Up @@ -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; }
Expand All @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
Expand Up @@ -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; }
Expand All @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
Expand Up @@ -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; }
Expand All @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
Expand Up @@ -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; }
Expand All @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
Expand Up @@ -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; }
Expand All @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
Expand Up @@ -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; }
Expand All @@ -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
Expand Down Expand Up @@ -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; }
Expand Down
21 changes: 21 additions & 0 deletions Tests/FluentAssertions.Equivalency.Specs/ExtensibilitySpecs.cs
Expand Up @@ -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<string>(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()
{
Expand Down
1 change: 1 addition & 0 deletions docs/_pages/releases.md
Expand Up @@ -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)
Expand Down

0 comments on commit 9fcaa69

Please sign in to comment.