diff --git a/Pipeline/Build.cs b/Pipeline/Build.cs
index a07a0cc08..25356ef59 100644
--- a/Pipeline/Build.cs
+++ b/Pipeline/Build.cs
@@ -19,7 +19,7 @@ partial class Build : NukeBuild
///
/// Afterward, you can update the package reference in `Directory.Packages.props` and reset this flag.
///
- readonly BuildScope BuildScope = BuildScope.Default;
+ readonly BuildScope BuildScope = BuildScope.CoreOnly;
[Parameter("Github Token")] readonly string GithubToken;
diff --git a/Source/aweXpect.Core/Equivalency/EquivalencyComparison.Compare.cs b/Source/aweXpect.Core/Equivalency/EquivalencyComparison.Compare.cs
index 0a1c1da99..9e7d86bee 100644
--- a/Source/aweXpect.Core/Equivalency/EquivalencyComparison.Compare.cs
+++ b/Source/aweXpect.Core/Equivalency/EquivalencyComparison.Compare.cs
@@ -198,19 +198,20 @@ private static async Task
if (typeOptions.Fields != IncludeMembers.None)
{
BindingFlags fieldBindingFlags = typeOptions.Fields.GetBindingFlags();
- foreach (string fieldName in expected.GetType().GetFields(typeOptions.Fields).Select(x => x.Name))
+ foreach (FieldInfo? fieldInfo in expected.GetType().GetFields(typeOptions.Fields))
{
memberCount++;
- string fieldMemberPath = ConcatMemberPath(memberPath, fieldName);
- if (typeOptions.MembersToIgnore.Contains(fieldMemberPath))
+ string fieldMemberPath = ConcatMemberPath(memberPath, fieldInfo.Name);
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(fieldMemberPath, fieldInfo.FieldType, fieldInfo)))
{
continue;
}
object? actualFieldValue =
- actual.GetType().GetField(fieldName, fieldBindingFlags)?.GetValue(actual);
+ actual.GetType().GetField(fieldInfo.Name, fieldBindingFlags)?.GetValue(actual);
object? expectedFieldValue =
- expected.GetType().GetField(fieldName, fieldBindingFlags)?.GetValue(expected);
+ expected.GetType().GetField(fieldInfo.Name, fieldBindingFlags)?.GetValue(expected);
if (!await Compare(actualFieldValue, expectedFieldValue,
options, options.GetTypeOptions(actualFieldValue?.GetType(), typeOptions),
@@ -224,20 +225,20 @@ private static async Task
if (typeOptions.Properties != IncludeMembers.None)
{
BindingFlags propertyBindingFlags = typeOptions.Properties.GetBindingFlags();
- foreach (string propertyName in expected.GetType().GetProperties(typeOptions.Properties)
- .Select(x => x.Name))
+ foreach (PropertyInfo? propertyInfo in expected.GetType().GetProperties(typeOptions.Properties))
{
memberCount++;
- string propertyMemberPath = ConcatMemberPath(memberPath, propertyName);
- if (typeOptions.MembersToIgnore.Contains(propertyMemberPath))
+ string propertyMemberPath = ConcatMemberPath(memberPath, propertyInfo.Name);
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(propertyMemberPath, propertyInfo.PropertyType, propertyInfo)))
{
continue;
}
- object? actualPropertyValue = actual.GetType().GetProperty(propertyName, propertyBindingFlags)
+ object? actualPropertyValue = actual.GetType().GetProperty(propertyInfo.Name, propertyBindingFlags)
?.GetValue(actual);
object? expectedPropertyValue =
- expected.GetType().GetProperty(propertyName, propertyBindingFlags)?.GetValue(expected);
+ expected.GetType().GetProperty(propertyInfo.Name, propertyBindingFlags)?.GetValue(expected);
if (!await Compare(actualPropertyValue, expectedPropertyValue,
options, options.GetTypeOptions(actualPropertyValue?.GetType(), typeOptions),
@@ -287,12 +288,13 @@ private static async Task
foreach (object? key in actual.Keys)
{
string elementMemberPath = $"{memberPath}[{key}]";
- if (typeOptions.MembersToIgnore.Contains(elementMemberPath))
+
+ object? actualObject = actual[key];
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(elementMemberPath, actualObject?.GetType() ?? typeof(object), null)))
{
continue;
}
-
- object? actualObject = actual[key];
if (expected.Contains(key))
{
object? expectedObject = expected[key];
@@ -328,14 +330,13 @@ private static async Task
}
string elementMemberPath = $"{memberPath}[{key}]";
- if (typeOptions.MembersToIgnore.Contains(elementMemberPath))
+ object? expectedObject = expected[key];
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(elementMemberPath, expectedObject?.GetType() ?? typeof(object), null)))
{
continue;
}
-
- object? expectedObject = expected[key];
-
failureBuilder.AppendLine();
if (failureBuilder.Length > 2)
{
@@ -385,12 +386,13 @@ private static async Task
for (int i = 0; i < Math.Min(actualObjects.Length, expectedObjects.Length); i++)
{
string elementMemberPath = $"{memberPath}[{(keys is null ? i : keys[i])}]";
- if (typeOptions.MembersToIgnore.Contains(elementMemberPath))
+ object? actualObject = actualObjects.ElementAtOrDefault(i);
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(elementMemberPath, actualObject?.GetType() ?? typeof(object), null)))
{
continue;
}
-
- object? actualObject = actualObjects.ElementAtOrDefault(i);
+
object? expectedObject = expectedObjects.ElementAtOrDefault(i);
if (!await Compare(actualObject, expectedObject,
@@ -406,13 +408,13 @@ private static async Task
for (int i = actualObjects.Length; i < expectedObjects.Length; i++)
{
string elementMemberPath = $"{memberPath}[{i}]";
- if (typeOptions.MembersToIgnore.Contains(elementMemberPath))
+ object? expectedObject = expectedObjects.ElementAtOrDefault(i);
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(elementMemberPath, expectedObject?.GetType() ?? typeof(object), null)))
{
continue;
}
- object? expectedObject = expectedObjects.ElementAtOrDefault(i);
-
failureBuilder.AppendLine();
if (failureBuilder.Length > 2)
{
@@ -432,13 +434,13 @@ private static async Task
for (int i = expectedObjects.Length; i < actualObjects.Length; i++)
{
string elementMemberPath = $"{memberPath}[{(keys is null ? i : keys[i])}]";
- if (typeOptions.MembersToIgnore.Contains(elementMemberPath))
+ object? actualObject = actualObjects.ElementAtOrDefault(i);
+ if (typeOptions.MembersToIgnore.Any(memberToIgnore
+ => memberToIgnore.IgnoreMember(elementMemberPath, actualObject?.GetType() ?? typeof(object), null)))
{
continue;
}
- object? actualObject = actualObjects.ElementAtOrDefault(i);
-
failureBuilder.AppendLine();
if (failureBuilder.Length > 2)
{
diff --git a/Source/aweXpect.Core/Equivalency/EquivalencyTypeOptions.cs b/Source/aweXpect.Core/Equivalency/EquivalencyTypeOptions.cs
index 2c56fc319..2153283a3 100644
--- a/Source/aweXpect.Core/Equivalency/EquivalencyTypeOptions.cs
+++ b/Source/aweXpect.Core/Equivalency/EquivalencyTypeOptions.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using System.Text;
namespace aweXpect.Equivalency;
@@ -18,7 +19,7 @@ public record EquivalencyTypeOptions
///
/// The members that should be ignored when checking for equivalency.
///
- public string[] MembersToIgnore { get; init; } = [];
+ public MemberToIgnore[] MembersToIgnore { get; init; } = [];
///
/// Specifies which fields to include in the object comparison.
diff --git a/Source/aweXpect.Core/Equivalency/MemberToIgnore.cs b/Source/aweXpect.Core/Equivalency/MemberToIgnore.cs
new file mode 100644
index 000000000..50354c1d3
--- /dev/null
+++ b/Source/aweXpect.Core/Equivalency/MemberToIgnore.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Reflection;
+
+namespace aweXpect.Equivalency;
+
+///
+/// Class to specify which members to ignore.
+///
+public abstract class MemberToIgnore
+{
+ ///
+ /// Checks if the member should be ignored.
+ ///
+ public abstract bool IgnoreMember(string memberPath, Type memberType, MemberInfo? memberInfo);
+
+ ///
+ /// Ignores all members that satisfy the .
+ ///
+ public class ByPredicate(Func predicate, string description) : MemberToIgnore
+ {
+ ///
+ public override bool IgnoreMember(string memberPath, Type memberType, MemberInfo? memberInfo)
+ => predicate(memberPath, memberType, memberInfo);
+
+ ///
+ public override string ToString() => description;
+ }
+
+ ///
+ /// Ignores all members that have the provided .
+ ///
+ public class ByName(string memberName) : MemberToIgnore
+ {
+ ///
+ public override bool IgnoreMember(string memberPath, Type memberType, MemberInfo? memberInfo)
+ => memberPath.EndsWith(memberName, StringComparison.OrdinalIgnoreCase);
+
+ ///
+ public override string ToString() => $"\"{memberName}\"";
+ }
+}
diff --git a/Source/aweXpect/Equivalency/EquivalencyOptionsExtensions.cs b/Source/aweXpect/Equivalency/EquivalencyOptionsExtensions.cs
index 55cff1487..2eb99a884 100644
--- a/Source/aweXpect/Equivalency/EquivalencyOptionsExtensions.cs
+++ b/Source/aweXpect/Equivalency/EquivalencyOptionsExtensions.cs
@@ -1,4 +1,6 @@
using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using aweXpect.Customization;
namespace aweXpect.Equivalency;
@@ -17,7 +19,82 @@ public static TEquivalencyOptions IgnoringMember(
where TEquivalencyOptions : EquivalencyTypeOptions
=> @this with
{
- MembersToIgnore = [..@this.MembersToIgnore, memberToIgnore,],
+ MembersToIgnore = [..@this.MembersToIgnore, new MemberToIgnore.ByName(memberToIgnore),],
+ };
+
+ ///
+ /// Ignores members matching the when checking for equivalency.
+ ///
+ public static TEquivalencyOptions Ignoring(
+ this TEquivalencyOptions @this,
+ Func predicate,
+ [CallerArgumentExpression("predicate")]
+ string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : EquivalencyTypeOptions
+ => @this with
+ {
+ MembersToIgnore =
+ [
+ ..@this.MembersToIgnore,
+ new MemberToIgnore.ByPredicate((memberName, memberType, _) => predicate(memberName, memberType),
+ doNotPopulateThisValue),
+ ],
+ };
+
+ ///
+ /// Ignores members matching the when checking for equivalency.
+ ///
+ public static TEquivalencyOptions Ignoring(
+ this TEquivalencyOptions @this,
+ Func predicate,
+ [CallerArgumentExpression("predicate")]
+ string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : EquivalencyTypeOptions
+ => @this with
+ {
+ MembersToIgnore =
+ [
+ ..@this.MembersToIgnore,
+ new MemberToIgnore.ByPredicate((memberName, _, _) => predicate(memberName),
+ doNotPopulateThisValue),
+ ],
+ };
+
+ ///
+ /// Ignores members matching the when checking for equivalency.
+ ///
+ public static TEquivalencyOptions Ignoring(
+ this TEquivalencyOptions @this,
+ Func predicate,
+ [CallerArgumentExpression("predicate")]
+ string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : EquivalencyTypeOptions
+ => @this with
+ {
+ MembersToIgnore =
+ [
+ ..@this.MembersToIgnore,
+ new MemberToIgnore.ByPredicate((_, memberType, _) => predicate(memberType),
+ doNotPopulateThisValue),
+ ],
+ };
+
+ ///
+ /// Ignores members matching the when checking for equivalency.
+ ///
+ public static TEquivalencyOptions Ignoring(
+ this TEquivalencyOptions @this,
+ Func predicate,
+ [CallerArgumentExpression("predicate")]
+ string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : EquivalencyTypeOptions
+ => @this with
+ {
+ MembersToIgnore =
+ [
+ ..@this.MembersToIgnore,
+ new MemberToIgnore.ByPredicate(predicate, doNotPopulateThisValue),
+ ],
};
///
diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
index 43c593949..5a96aa35b 100644
--- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
+++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_net8.0.txt
@@ -1236,6 +1236,14 @@ namespace aweXpect.Equivalency
}
public static class EquivalencyOptionsExtensions
{
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
public static TEquivalencyOptions IgnoringCollectionOrder(this TEquivalencyOptions @this, bool ignoreCollectionOrder = true)
where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
public static TEquivalencyOptions IgnoringMember(this TEquivalencyOptions @this, string memberToIgnore)
diff --git a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt
index f319217a3..60b8d470a 100644
--- a/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt
+++ b/Tests/aweXpect.Api.Tests/Expected/aweXpect_netstandard2.0.txt
@@ -1182,6 +1182,14 @@ namespace aweXpect.Equivalency
}
public static class EquivalencyOptionsExtensions
{
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
+ public static TEquivalencyOptions Ignoring(this TEquivalencyOptions @this, System.Func predicate, [System.Runtime.CompilerServices.CallerArgumentExpression("predicate")] string doNotPopulateThisValue = "")
+ where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
public static TEquivalencyOptions IgnoringCollectionOrder(this TEquivalencyOptions @this, bool ignoreCollectionOrder = true)
where TEquivalencyOptions : aweXpect.Equivalency.EquivalencyTypeOptions { }
public static TEquivalencyOptions IgnoringMember(this TEquivalencyOptions @this, string memberToIgnore)
diff --git a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt
index fe3b8fd56..6ddc0b724 100644
--- a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt
+++ b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_net8.0.txt
@@ -568,7 +568,7 @@ namespace aweXpect.Equivalency
public aweXpect.Equivalency.EquivalencyComparisonType? ComparisonType { get; init; }
public aweXpect.Equivalency.IncludeMembers Fields { get; init; }
public bool IgnoreCollectionOrder { get; init; }
- public string[] MembersToIgnore { get; init; }
+ public aweXpect.Equivalency.MemberToIgnore[] MembersToIgnore { get; init; }
public aweXpect.Equivalency.IncludeMembers Properties { get; init; }
}
[System.Flags]
@@ -590,6 +590,23 @@ namespace aweXpect.Equivalency
public override string? ToString() { }
}
}
+ public abstract class MemberToIgnore
+ {
+ protected MemberToIgnore() { }
+ public abstract bool IgnoreMember(string memberPath, System.Type memberType, System.Reflection.MemberInfo? memberInfo);
+ public class ByName : aweXpect.Equivalency.MemberToIgnore
+ {
+ public ByName(string memberName) { }
+ public override bool IgnoreMember(string memberPath, System.Type memberType, System.Reflection.MemberInfo? memberInfo) { }
+ public override string ToString() { }
+ }
+ public class ByPredicate : aweXpect.Equivalency.MemberToIgnore
+ {
+ public ByPredicate(System.Func predicate, string description) { }
+ public override bool IgnoreMember(string memberPath, System.Type memberType, System.Reflection.MemberInfo? memberInfo) { }
+ public override string ToString() { }
+ }
+ }
}
namespace aweXpect
{
diff --git a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt
index d0be10ada..7fc520898 100644
--- a/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt
+++ b/Tests/aweXpect.Core.Api.Tests/Expected/aweXpect.Core_netstandard2.0.txt
@@ -554,7 +554,7 @@ namespace aweXpect.Equivalency
public aweXpect.Equivalency.EquivalencyComparisonType? ComparisonType { get; init; }
public aweXpect.Equivalency.IncludeMembers Fields { get; init; }
public bool IgnoreCollectionOrder { get; init; }
- public string[] MembersToIgnore { get; init; }
+ public aweXpect.Equivalency.MemberToIgnore[] MembersToIgnore { get; init; }
public aweXpect.Equivalency.IncludeMembers Properties { get; init; }
}
[System.Flags]
@@ -576,6 +576,23 @@ namespace aweXpect.Equivalency
public override string? ToString() { }
}
}
+ public abstract class MemberToIgnore
+ {
+ protected MemberToIgnore() { }
+ public abstract bool IgnoreMember(string memberPath, System.Type memberType, System.Reflection.MemberInfo? memberInfo);
+ public class ByName : aweXpect.Equivalency.MemberToIgnore
+ {
+ public ByName(string memberName) { }
+ public override bool IgnoreMember(string memberPath, System.Type memberType, System.Reflection.MemberInfo? memberInfo) { }
+ public override string ToString() { }
+ }
+ public class ByPredicate : aweXpect.Equivalency.MemberToIgnore
+ {
+ public ByPredicate(System.Func predicate, string description) { }
+ public override bool IgnoreMember(string memberPath, System.Type memberType, System.Reflection.MemberInfo? memberInfo) { }
+ public override string ToString() { }
+ }
+ }
}
namespace aweXpect
{
diff --git a/Tests/aweXpect.Core.Tests/Equivalency/EquivalencyOptionsExtensionsTests.cs b/Tests/aweXpect.Core.Tests/Equivalency/EquivalencyOptionsExtensionsTests.cs
index ec0bd673d..8f496a9cf 100644
--- a/Tests/aweXpect.Core.Tests/Equivalency/EquivalencyOptionsExtensionsTests.cs
+++ b/Tests/aweXpect.Core.Tests/Equivalency/EquivalencyOptionsExtensionsTests.cs
@@ -4,6 +4,92 @@ namespace aweXpect.Core.Tests.Equivalency;
public sealed class EquivalencyOptionsExtensionsTests
{
+ [Fact]
+ public async Task Generic_For_Ignoring_StringAndTypePredicate_ShouldSetOptionForType()
+ {
+ EquivalencyOptions options = new();
+
+ EquivalencyOptions result =
+ options.For(o => o.Ignoring((n, t) => n.EndsWith("At") && t == typeof(DateTime)));
+
+ await That(result.MembersToIgnore).IsEmpty();
+ await That(result.CustomOptions).ContainsKey(typeof(MyClass))
+ .WhoseValue.IsEquivalentTo(new
+ {
+ MembersToIgnore = It.Is().That.HasCount(1),
+ });
+ await That(result.ToString()).IsEqualTo("""
+ - include public fields and properties
+ - for EquivalencyOptionsExtensionsTests.MyClass:
+ - include public fields and properties
+ - ignore members: [(n, t) => n.EndsWith("At") && t == typeof(DateTime)]
+ """);
+ }
+
+ [Fact]
+ public async Task Generic_For_Ignoring_StringPredicate_ShouldSetOptionForType()
+ {
+ EquivalencyOptions options = new();
+
+ EquivalencyOptions result = options.For(o => o.Ignoring(x => x == "foo"));
+
+ await That(result.MembersToIgnore).IsEmpty();
+ await That(result.CustomOptions).ContainsKey(typeof(MyClass))
+ .WhoseValue.IsEquivalentTo(new
+ {
+ MembersToIgnore = It.Is().That.HasCount(1),
+ });
+ await That(result.ToString()).IsEqualTo("""
+ - include public fields and properties
+ - for EquivalencyOptionsExtensionsTests.MyClass:
+ - include public fields and properties
+ - ignore members: [x => x == "foo"]
+ """);
+ }
+
+ [Fact]
+ public async Task Generic_For_Ignoring_StringTypeAndMemberInfoPredicate_ShouldSetOptionForType()
+ {
+ EquivalencyOptions options = new();
+
+ EquivalencyOptions result = options.For(o
+ => o.Ignoring((n, t, f) => n.EndsWith("At") && t == typeof(DateTime) && f != null));
+
+ await That(result.MembersToIgnore).IsEmpty();
+ await That(result.CustomOptions).ContainsKey(typeof(MyClass))
+ .WhoseValue.IsEquivalentTo(new
+ {
+ MembersToIgnore = It.Is().That.HasCount(1),
+ });
+ await That(result.ToString()).IsEqualTo("""
+ - include public fields and properties
+ - for EquivalencyOptionsExtensionsTests.MyClass:
+ - include public fields and properties
+ - ignore members: [(n, t, f) => n.EndsWith("At") && t == typeof(DateTime) && f != null]
+ """);
+ }
+
+ [Fact]
+ public async Task Generic_For_Ignoring_TypePredicate_ShouldSetOptionForType()
+ {
+ EquivalencyOptions options = new();
+
+ EquivalencyOptions result = options.For(o => o.Ignoring(x => x == typeof(DateTime)));
+
+ await That(result.MembersToIgnore).IsEmpty();
+ await That(result.CustomOptions).ContainsKey(typeof(MyClass))
+ .WhoseValue.IsEquivalentTo(new
+ {
+ MembersToIgnore = It.Is().That.HasCount(1),
+ });
+ await That(result.ToString()).IsEqualTo("""
+ - include public fields and properties
+ - for EquivalencyOptionsExtensionsTests.MyClass:
+ - include public fields and properties
+ - ignore members: [x => x == typeof(DateTime)]
+ """);
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]
@@ -45,7 +131,7 @@ await That(result.CustomOptions).ContainsKey(typeof(MyClass))
{
MembersToIgnore = new[]
{
- memberToIgnore,
+ new MemberToIgnore.ByName(memberToIgnore),
},
});
await That(result.ToString()).IsEqualTo($"""
diff --git a/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.Tests.cs b/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.Tests.cs
index 028435678..a44884aa8 100644
--- a/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.Tests.cs
+++ b/Tests/aweXpect.Tests/Objects/ThatObject.IsEquivalentTo.Tests.cs
@@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
+using System.Reflection;
using aweXpect.Equivalency;
// ReSharper disable UnusedMember.Local
@@ -31,6 +32,143 @@ async Task Act()
await That(Act).DoesNotThrow();
}
+ [Fact]
+ public async Task IgnoringMismatchingProperties_ShouldBeEquivalent()
+ {
+ OuterClass subject = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 1,
+ },
+ };
+ OuterClass expected = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 2,
+ },
+ };
+
+ async Task Act()
+ => await That(subject).IsEquivalentTo(expected, o => o
+ .For(x => x.IgnoringMember("IntValue")));
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task IgnoringMismatchingPropertiesByPathAndTypePredicate_ShouldBeEquivalent()
+ {
+ OuterClass subject = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 1,
+ },
+ };
+ OuterClass expected = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 2,
+ },
+ };
+
+ async Task Act()
+ => await That(subject).IsEquivalentTo(expected, o => o
+ .Ignoring((memberPath, memberType)
+ => memberPath.EndsWith("IntValue") && memberType == typeof(int)));
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task IgnoringMismatchingPropertiesByPathPredicate_ShouldBeEquivalent()
+ {
+ OuterClass subject = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 1,
+ },
+ };
+ OuterClass expected = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 2,
+ },
+ };
+
+ async Task Act()
+ => await That(subject).IsEquivalentTo(expected, o => o
+ .Ignoring(memberPath => memberPath == "Inner.IntValue"));
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task IgnoringMismatchingPropertiesByPathTypeAndMemberInfoPredicate_ShouldBeEquivalent()
+ {
+ OuterClass subject = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 1,
+ },
+ };
+ OuterClass expected = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 2,
+ },
+ };
+
+ async Task Act()
+ => await That(subject).IsEquivalentTo(expected, o => o
+ .Ignoring((memberPath, _, memberInfo)
+ => memberPath.EndsWith("IntValue") && memberInfo is PropertyInfo));
+
+ await That(Act).DoesNotThrow();
+ }
+
+ [Fact]
+ public async Task IgnoringMismatchingPropertiesByTypePredicate_ShouldBeEquivalent()
+ {
+ OuterClass subject = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 1,
+ },
+ };
+ OuterClass expected = new()
+ {
+ Value = "Foo",
+ Inner = new InnerClass
+ {
+ IntValue = 2,
+ },
+ };
+
+ async Task Act()
+ => await That(subject).IsEquivalentTo(expected, o => o
+ .Ignoring(memberType => memberType == typeof(int)));
+
+ await That(Act).DoesNotThrow();
+ }
+
[Fact]
public async Task MismatchedObjects_ShouldNotBeEquivalent()
{
@@ -142,8 +280,10 @@ is equivalent to ThatObject.OuterClass {
"4"
],
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
@@ -244,8 +384,10 @@ is equivalent to ThatObject.OuterClass {
"4"
],
Inner = ,
+ IntValue = 0,
Value = "Bart"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
@@ -334,8 +476,10 @@ is equivalent to ThatObject.OuterClass {
Inner = ThatObject.InnerClass {
Collection = ,
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
@@ -1144,11 +1288,12 @@ is not equivalent to ThatObject.OuterClass {
Inner = ThatObject.InnerClass {
Collection = ,
Inner = ,
+ IntValue = 0,
Value =
},
Value = "Foo"
}
-
+
Equivalency options:
- include public fields and properties
- ignore members: ["Inner"]
diff --git a/Tests/aweXpect.Tests/Objects/ThatObject.IsNotEquivalentTo.Tests.cs b/Tests/aweXpect.Tests/Objects/ThatObject.IsNotEquivalentTo.Tests.cs
index f4988535c..4b07a9388 100644
--- a/Tests/aweXpect.Tests/Objects/ThatObject.IsNotEquivalentTo.Tests.cs
+++ b/Tests/aweXpect.Tests/Objects/ThatObject.IsNotEquivalentTo.Tests.cs
@@ -106,8 +106,10 @@ is not equivalent to ThatObject.OuterClass {
"4"
],
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
@@ -122,13 +124,15 @@ is not equivalent to ThatObject.OuterClass {
"3"
],
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
}
-
+
Equivalency options:
- include public fields and properties
- ignore members: ["Inner.Inner.Collection[3]"]
@@ -214,8 +218,10 @@ is not equivalent to ThatObject.OuterClass {
Inner = ThatObject.InnerClass {
Collection = ,
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
@@ -226,13 +232,15 @@ is not equivalent to ThatObject.OuterClass {
Inner = ThatObject.InnerClass {
Collection = ,
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
}
-
+
Equivalency options:
- include public fields and properties
""");
@@ -284,8 +292,10 @@ is not equivalent to ThatObject.OuterClass {
"3"
],
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
@@ -300,13 +310,15 @@ is not equivalent to ThatObject.OuterClass {
"3"
],
Inner = ,
+ IntValue = 0,
Value = "Baz"
},
+ IntValue = 0,
Value = "Bar"
},
Value = "Foo"
}
-
+
Equivalency options:
- include public fields and properties
""");
diff --git a/Tests/aweXpect.Tests/Objects/ThatObject.cs b/Tests/aweXpect.Tests/Objects/ThatObject.cs
index c69cb773d..474b6f82a 100644
--- a/Tests/aweXpect.Tests/Objects/ThatObject.cs
+++ b/Tests/aweXpect.Tests/Objects/ThatObject.cs
@@ -21,6 +21,8 @@ private sealed class InnerClass
public InnerClass? Inner { get; set; }
public string? Value { get; set; }
+
+ public int IntValue { get; set; }
}
private sealed class OuterClass