-
Notifications
You must be signed in to change notification settings - Fork 731
Description
Description
When asserting Should().BeEquivalentTo on a model that has 2-way references, with the IgnoringCyclicReferences, the assertion doesn't detect that the actual model objects don't have the cyclic reference, if the expected model has a cyclic or back-reference. While the equivalency assertion the other way around does detect the difference.
See the code example, but in short:
- Expected model: Parent references Child, and Child references parent
- Actual model: Parent references Child, but Child doesn't reference parent.
The weird thing is, when doing
actualParent.Should().BeEquivalentTo(expectedParent, options => options.IgnoringCyclicReferences())
it does not detect the difference between both models,
while when doing
expectedParent.Should().BeEquivalentTo(actualParent, options => options.IgnoringCyclicReferences())
it does detect the difference.
I would expect that equivalency works both ways.
Reproduction Steps
using FluentAssertions;
using Xunit;
using Xunit.Sdk;
namespace FluentAssertionsExperiment;
public class EquivalencyOptionsIgnoreCyclicTestsSimplified
{
[Fact]
public void IgnoringCyclicReferences_BackReferenceIsNull_ActualEquivalentToExpected_DoesNotThrow()
{
// Arrange expected model
var expectedChild = new Child() {Id = 1};
var expectedParent = new Parent(){ Id = 100, Children = new List<Child>(){expectedChild}};
expectedChild.Parent = expectedParent;
// Arrange actual model
var actualChild = new Child() { Id = 1 };
var actualParent = new Parent() { Id = 100, Children = new List<Child>() { actualChild } };
//actualChild.Parent = actualParent; // => this is cleary different between both models
// Assert actual model be equivalent to expected model => Does not throw.
var action1 = () => actualParent.Should().BeEquivalentTo(expectedParent, options => options.IgnoringCyclicReferences());
action1.Should().NotThrow();
// Assert expected model be equivalent to actual model => Does throw.
var action2 = () => expectedParent.Should().BeEquivalentTo(actualParent, options => options.IgnoringCyclicReferences());
action2.Should().Throw<XunitException>();
}
private class Parent
{
public int Id { get; set; }
public List<Child> Children { get; set; }
}
private class Child
{
public int Id { get; set; }
public Parent Parent { get; set; }
}
}Expected behavior
Should.BeEquivalent() should throw in both directions.
Actual behavior
Should.BeEquivalent() throws only when the expected model doesn't have the cyclic reference, and does NOT throw when the expected model has the cyclic reference.
Regression?
Don't know.
Known Workarounds
Do the Should().BeEquivalent both ways in your test.
Configuration
Tested with .Net 8, and version 6.12.1 of FluentAssertions.
Other information
I tested this with a few different setups:
- Parent has collection of Children as reference
- Parent has single item reference to Child
- Similar behavior on the 2nd level from the root (added GrandChild level)
- Setting the Child.Parent property to a different Parent, instead of leaving empty.
I can provide testcode for these scenarios if needed.
I am willing to help test solutions, or doing some code-analysis, if I get some pointers to where to start.
Are you willing to help with a pull-request?
No