Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 230 additions & 0 deletions TUnit.Assertions.Tests/IgnoringTypeEquivalentTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
namespace TUnit.Assertions.Tests;

public class IgnoringTypeEquivalentTests
{
[Test]
public async Task IgnoringType_Generic_DateTime_Properties_Are_Ignored()
{
var object1 = new MyClassWithDates
{
Name = "Test",
CreatedDate = new DateTime(2023, 1, 1),
ModifiedDate = new DateTime(2023, 1, 2),
Value = 123
};

var object2 = new MyClassWithDates
{
Name = "Test",
CreatedDate = new DateTime(2024, 6, 15),
ModifiedDate = new DateTime(2024, 7, 20),
Value = 123
};

await TUnitAssert.That(object1)
.IsEquivalentTo(object2)
.IgnoringType<DateTime>();
}

[Test]
public async Task IgnoringType_NonGeneric_DateTime_Properties_Are_Ignored()
{
var object1 = new MyClassWithDates
{
Name = "Test",
CreatedDate = new DateTime(2023, 1, 1),
ModifiedDate = new DateTime(2023, 1, 2),
Value = 123
};

var object2 = new MyClassWithDates
{
Name = "Test",
CreatedDate = new DateTime(2024, 6, 15),
ModifiedDate = new DateTime(2024, 7, 20),
Value = 123
};

await TUnitAssert.That(object1)
.IsEquivalentTo(object2)
.IgnoringType(typeof(DateTime));
}

[Test]
public async Task IgnoringType_Nullable_DateTime_Properties_Are_Ignored()
{
var object1 = new MyClassWithNullableDates
{
Name = "Test",
OptionalDate = new DateTime(2023, 1, 1),
Value = 123
};

var object2 = new MyClassWithNullableDates
{
Name = "Test",
OptionalDate = new DateTime(2024, 6, 15),
Value = 123
};

await TUnitAssert.That(object1)
.IsEquivalentTo(object2)
.IgnoringType<DateTime>();
}

[Test]
public async Task IgnoringType_DateTimeOffset_Properties_Are_Ignored()
{
var object1 = new MyClassWithDateTimeOffset
{
Name = "Test",
Timestamp = new DateTimeOffset(2023, 1, 1, 0, 0, 0, TimeSpan.Zero),
Value = 123
};

var object2 = new MyClassWithDateTimeOffset
{
Name = "Test",
Timestamp = new DateTimeOffset(2024, 6, 15, 12, 30, 45, TimeSpan.FromHours(2)),
Value = 123
};

await TUnitAssert.That(object1)
.IsEquivalentTo(object2)
.IgnoringType<DateTimeOffset>();
}

[Test]
public async Task IgnoringType_Multiple_Types_Can_Be_Ignored()
{
var object1 = new MyClassWithMultipleTypes
{
Name = "Test",
CreatedDate = new DateTime(2023, 1, 1),
Guid = Guid.NewGuid(),
Value = 123
};

var object2 = new MyClassWithMultipleTypes
{
Name = "Test",
CreatedDate = new DateTime(2024, 6, 15),
Guid = Guid.NewGuid(),
Value = 123
};

await TUnitAssert.That(object1)
.IsEquivalentTo(object2)
.IgnoringType<DateTime>()
.IgnoringType<Guid>();
}

[Test]
public async Task IgnoringType_Fields_Are_Also_Ignored()
{
var object1 = new MyClassWithDateFields
{
Name = "Test",
CreatedDateField = new DateTime(2023, 1, 1),
Value = 123
};

var object2 = new MyClassWithDateFields
{
Name = "Test",
CreatedDateField = new DateTime(2024, 6, 15),
Value = 123
};

await TUnitAssert.That(object1)
.IsEquivalentTo(object2)
.IgnoringType<DateTime>();
}

[Test]
public async Task NotEquivalentTo_IgnoringType_Works_Correctly()
{
var object1 = new MyClassWithDates
{
Name = "Different",
CreatedDate = new DateTime(2023, 1, 1),
ModifiedDate = new DateTime(2023, 1, 2),
Value = 123
};

var object2 = new MyClassWithDates
{
Name = "Test",
CreatedDate = new DateTime(2024, 6, 15),
ModifiedDate = new DateTime(2024, 7, 20),
Value = 123
};

await TUnitAssert.That(object1)
.IsNotEquivalentTo(object2)
.IgnoringType<DateTime>();
}

[Test]
public async Task IgnoringType_Without_Matching_Type_Still_Compares_All_Properties()
{
var object1 = new MyClassWithoutDates
{
Name = "Test",
Value = 123
};

var object2 = new MyClassWithoutDates
{
Name = "Different",
Value = 123
};

await TUnitAssert.That(object1)
.IsNotEquivalentTo(object2)
.IgnoringType<DateTime>();
}

private class MyClassWithDates
{
public string Name { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public int Value { get; set; }
}

private class MyClassWithNullableDates
{
public string Name { get; set; } = string.Empty;
public DateTime? OptionalDate { get; set; }
public int Value { get; set; }
}

private class MyClassWithDateTimeOffset
{
public string Name { get; set; } = string.Empty;
public DateTimeOffset Timestamp { get; set; }
public int Value { get; set; }
}

private class MyClassWithMultipleTypes
{
public string Name { get; set; } = string.Empty;
public DateTime CreatedDate { get; set; }
public Guid Guid { get; set; }
public int Value { get; set; }
}

private class MyClassWithDateFields
{
public string Name { get; set; } = string.Empty;
public DateTime CreatedDateField;
public int Value { get; set; }
}

private class MyClassWithoutDates
{
public string Name { get; set; } = string.Empty;
public int Value { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ public EquivalentToAssertionBuilderWrapper<TActual, TExpected> IgnoringMember(st
return this;
}

public EquivalentToAssertionBuilderWrapper<TActual, TExpected> IgnoringType<TType>()
{
var assertion = (EquivalentToExpectedValueAssertCondition<TActual, TExpected>) Assertions.Peek();

assertion.IgnoringType(typeof(TType));

AppendCallerMethod([$"<{typeof(TType).Name}>"]);

return this;
}

public EquivalentToAssertionBuilderWrapper<TActual, TExpected> IgnoringType(Type type, [CallerArgumentExpression(nameof(type))] string doNotPopulateThis = "")
{
var assertion = (EquivalentToExpectedValueAssertCondition<TActual, TExpected>) Assertions.Peek();

assertion.IgnoringType(type);

AppendCallerMethod([doNotPopulateThis]);

return this;
}

public EquivalentToAssertionBuilderWrapper<TActual, TExpected> WithPartialEquivalency()
{
var assertion = (EquivalentToExpectedValueAssertCondition<TActual, TExpected>) Assertions.Peek();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ public NotEquivalentToAssertionBuilderWrapper<TActual, TExpected> IgnoringMember
return this;
}

public NotEquivalentToAssertionBuilderWrapper<TActual, TExpected> IgnoringType<TType>()
{
var assertion = (NotEquivalentToExpectedValueAssertCondition<TActual, TExpected>) Assertions.Peek();

assertion.IgnoringType(typeof(TType));

AppendCallerMethod([$"<{typeof(TType).Name}>"]);

return this;
}

public NotEquivalentToAssertionBuilderWrapper<TActual, TExpected> IgnoringType(Type type, [CallerArgumentExpression(nameof(type))] string doNotPopulateThis = "")
{
var assertion = (NotEquivalentToExpectedValueAssertCondition<TActual, TExpected>) Assertions.Peek();

assertion.IgnoringType(type);

AppendCallerMethod([doNotPopulateThis]);

return this;
}

public NotEquivalentToAssertionBuilderWrapper<TActual, TExpected> WithPartialEquivalency()
{
var assertion = (NotEquivalentToExpectedValueAssertCondition<TActual, TExpected>) Assertions.Peek();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class EquivalentToExpectedValueAssertCondition<
TExpected>(TExpected expected, string? expectedExpression) : ExpectedValueAssertCondition<TActual, TExpected>(expected)
{
private readonly List<string> _ignoredMembers = [];
private readonly List<Type> _ignoredTypes = [];

public EquivalencyKind EquivalencyKind { get; set; } = EquivalencyKind.Full;

Expand Down Expand Up @@ -62,6 +63,7 @@ protected override ValueTask<AssertionResult> GetResult(TActual? actualValue, TE
var failures = Compare.CheckEquivalent(actualValue, ExpectedValue, new CompareOptions
{
MembersToIgnore = [.. _ignoredMembers],
TypesToIgnore = [.. _ignoredTypes],
EquivalencyKind = EquivalencyKind
}, null).ToList();

Expand Down Expand Up @@ -95,4 +97,9 @@ public void IgnoringMember(string fieldName)
{
_ignoredMembers.Add(fieldName);
}

public void IgnoringType(Type type)
{
_ignoredTypes.Add(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class NotEquivalentToExpectedValueAssertCondition<
TExpected>(TExpected expected, string? expectedExpression) : ExpectedValueAssertCondition<TActual, TExpected>(expected)
{
private readonly List<string> _ignoredMembers = [];
private readonly List<Type> _ignoredTypes = [];

public EquivalencyKind EquivalencyKind { get; set; } = EquivalencyKind.Full;

Expand Down Expand Up @@ -45,6 +46,7 @@ protected override ValueTask<AssertionResult> GetResult(TActual? actualValue, TE
new CompareOptions
{
MembersToIgnore = [.. _ignoredMembers],
TypesToIgnore = [.. _ignoredTypes],
EquivalencyKind = EquivalencyKind,
});

Expand Down Expand Up @@ -81,6 +83,7 @@ protected override ValueTask<AssertionResult> GetResult(TActual? actualValue, TE
var failures = Compare.CheckEquivalent(actualValue, ExpectedValue, new CompareOptions
{
MembersToIgnore = [.. _ignoredMembers],
TypesToIgnore = [.. _ignoredTypes],
EquivalencyKind = EquivalencyKind
}, null).ToList();

Expand All @@ -101,4 +104,9 @@ public void IgnoringMember(string fieldName)
{
_ignoredMembers.Add(fieldName);
}

public void IgnoringType(Type type)
{
_ignoredTypes.Add(type);
}
}
Loading
Loading