Skip to content

ObjectAssertions.Be no longer uses IComparable<T> and does not use IEquatable<T> #1675

@kwbauer

Description

@kwbauer

Description

Version 6.0.0 introduced an undocumented breaking change from version 5.10.3. In older versions, actual.Should().Be(expected) would pass when actual and expected were of the same type and that type implemented IComparable<T> and the CompareTo implementation returned 0. Tests that relied on this now fail in versions 6.0.0 and 6.1.0.

Interestingly, NUnit's Assert.AreEqual fails to pass in this situation as well but it does pass if the class implements IEquatable<T> and the Equals implementation returns true.

Neither the older or newer versions of FluentAssertions will pass Should().Be() for the IEquatable<T> scenario.

Complete minimal example reproducing the issue

using FluentAssertions;
using NUnit.Framework;

namespace FluentAssertionBug
{
    public class ClassToBeComparedWithIComparable : IComparable<ClassToBeComparedWithIComparable>
    {
        public string Id { get; set; }
        public int Value { get; set; }

        public int CompareTo(ClassToBeComparedWithIComparable other)
        {
            var c = string.Compare(Id, other.Id, StringComparison.Ordinal);


            return c != 0 ? c : Value.CompareTo(other.Value);
        }
    }

    public class ClassToBeComparedWithIEquatable : IEquatable<ClassToBeComparedWithIEquatable>
    {
        public string Id { get; set; }
        public int Value { get; set; }

        public bool Equals(ClassToBeComparedWithIEquatable other)
        {
            if (other == null)
                return false;

            return Id.Equals(other.Id) && Value.Equals(other.Value);
        }
    }

    [TestFixture]
    public class Tests
    {
        [Test]
        public void GivenTwoIComparablesThatHaveIdenticalValues_ShouldBe_ShouldPass_WillFailInNewerVersions()
        {
            var value1 = new ClassToBeComparedWithIComparable { Id = "One", Value = 1 };
            var value2 = new ClassToBeComparedWithIComparable { Id = "One", Value = 1 };

            value1.Should().Be(value2);
        }

        [Test]
        public void GivenTwoIEquatablesThatHaveIdenticalValues_Equals_ShouldPass()
        {
            var value1 = new ClassToBeComparedWithIEquatable { Id = "One", Value = 1 };
            var value2 = new ClassToBeComparedWithIEquatable { Id = "One", Value = 1 };

            if (value1.Equals(value2))
                Assert.Pass();

            Assert.Fail();
        }

        [Test]
        public void GivenTwoIEquatablesThatHaveIdenticalValues_AssertAreEqual_ShouldPass()
        {
            var value1 = new ClassToBeComparedWithIEquatable { Id = "One", Value = 1 };
            var value2 = new ClassToBeComparedWithIEquatable { Id = "One", Value = 1 };

            Assert.AreEqual(value2, value1);
        }
    }
}

Expected behavior:

I expect the breaking change for the IComparable<T> scenario to be documented.
I expect Should().Be() to honor the IEquatable<T> implementation when determining if 2 objects are equal.

Actual behavior:

Neither the 6.0.0 nor the 6.1.0 documentation mention that Should().Be() no longer honors the IComparable<T> implementation when determining equality.

Should().Be() does not honor the IEquatable<T> implementation when determining equality.

Versions

  • I upgraded from FluentAssertion 5.10.3 to 6.1.0. (I also tried using 6.0.0 to determine when the break first occurred.)
  • I am using .Net Core 3.1
  • I am using NUnit 3.13.2 as my testing suite.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions