4

I have a nested class and FluentAssertions can assert them.
Then I change class to struct and the test fails.

( If I change IEnumerable<ItemStruct> MyItems { get; set; } to ItemStruct MyItem { get; set; } the comparison succeeds in both cases. So I guess it has to do with the IEnumerable. )

using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;

namespace CowsCannotReadLogs.FileHandling.UnitTest
{
    [TestClass]
    public class TestFluentAssertionStruct
    {
        public struct DataStruct
        {
            public IEnumerable<string> MyItems { get; set; }
        }

        // Fails.
        [TestMethod]
        public void TestSimpleStruct()
        {
            var data1 = new DataStruct { MyItems = new[] { "A" } };
            var data2 = new DataStruct { MyItems = new[] { "A" } };
            data1.Should().BeEquivalentTo(data2);
        }

        // Fails.
        [TestMethod]
        public void TestSimpleStructWithComparingByValue()
        {
            var data1 = new DataStruct { MyItems = new[] { "A" } };
            var data2 = new DataStruct { MyItems = new[] { "A" } };
            data1.Should().BeEquivalentTo(data2, 
                options => options.ComparingByValue<DataStruct>());
        }


        public class DataClass
        {
            public IEnumerable<string> MyItems { get; set; }
        }

        // Succeeds.
        [TestMethod]
        public void TestSimpleClass()
        {
            var data1 = new DataClass { MyItems = new[] { "A" } };
            var data2 = new DataClass { MyItems = new[] { "A" } };
            data1.Should().BeEquivalentTo(data2);
        }
    }
}
LosManos
  • 7,195
  • 6
  • 56
  • 107
  • 3
    If you check FluentAssertion's documentation ("Object graph comparison") you'll see that the method `BeEquivalentTo` compares two instances by *value semantics*, checking each one of it's properties **if** the type being compared overrides `Obejct.Equal`. This is not true for your value types, so they will be compared with the default equality operator for value types, which will succeed if your struct only has other struct properties **but** will fail when you have a reference type (IEnumerable) as one of its properties. I suspect that using `ComparingByValue` would solve it. – IPValverde Apr 04 '19 at 20:20
  • @IPValverde Thanks for the [ComparingByValue<>](https://fluentassertions.com/documentation/#value-types) information. It still does not work though. I updated the example. – LosManos Apr 05 '19 at 05:19
  • 2
    Try using `ComparingByMembers()` instead. – Jonas Nyrup Apr 05 '19 at 06:20

1 Answers1

6

When using BeEquivalentTo on structs, the two instances are by default compared by value semantics. to compare the structs by their members, the default behavior can be overridden either locally or globally.

Locally inside a test:

[TestMethod]
public void Test()
{
    subject.Should().BeEquivalentTo(expected, 
        opt => opt.ComparingByMembers<DataStruct>());
}

Globally in e.g. [AssemblyInitialize]:

[AssemblyInitialize]
public static void AssemblyInitialize(TestContext context)
{
    AssertionOptions.AssertEquivalencyUsing(opt => opt.ComparingByMembers<DataStruct>());
}

Jonas Nyrup
  • 2,376
  • 18
  • 25