Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend IObjectInfo with info about DeclaringType #1950

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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; }
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved

/// <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; }
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
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; }
dennisdoomen marked this conversation as resolved.
Show resolved Hide resolved
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