Search code examples
c#nunit.net-4.5

c# Nunit .Is.EquivalentTo not behaving as expected for a List<T>


I am using NUnit in .net 4.5.2 project

Using the Collection assertions thusly:

Assert.That(first, Is.EquivalentTo(second));

This unit test passed

        [Test] 
        public void Test_Int_ExampleDeepListCompare()
        {


            List<List<string>> first = new List<List<string>>()
            {
                new List<string> { "1", "3" },
                new List<string> { "1", "2" },
                new List<string> { "a", "b" }
            };

            List<List<string>> second = new List<List<string>>()
            {
                new List<string> { "1", "2" },
                new List<string> { "1", "3" },
                new List<string> { "a", "b" }
            };


            
            Assert.That(first, Is.EquivalentTo(second));
        }

So then I used it on one of our classes that look equivalent in the debugger and it failed.

To test that, I created a simple reproduction, which still fails, now I'm really confused

    [Test]
    public void Test_Int_ExampleDeepCompareCustomObjects2()
    {
        List<SimpleObject> rtnValFakeA = new List<SimpleObject>() { new SimpleObject() { FirstName = "Bob", LastName = "Jones", Mi = "a", StudId = 12345 } };
        List<SimpleObject> rtnValFakeb = new List<SimpleObject>() { new SimpleObject() { FirstName = "Bob", LastName = "Jones", Mi = "a", StudId = 12345 } };

        //assert with deep compare ignoring order - EWB
        Assert.That(rtnValFakeA, Is.EquivalentTo(rtnValFakeb));
    }

The object definition, used in example two, I figure has to be something here:

public class SimpleObject
{
    public string LastName { get; set; }

    public string FirstName { get; set; }

    public string Mi { get; set; }

    public Int64 StudId { get; set; }

}

The second test fails with the message:

Expected: equivalent to < <_Test_DAL.SimpleObject> > But was: < <_Test_DAL.SimpleObject> >

at NUnit.Framework.Assert.That(Object actual, IResolveConstraint expression, String message, Object[] args) at _Test_DAL.TestADStudentDAL.Test_Int_ExampleDeepCompareCustomObjects2() in d:\TFS\JCDCHelper\2013\JCDCHelper.DAL_Tests\DAL\TestADStudentDAL.cs:line 152

But I totally expected it to pass

Why does the first pass, and the second fail? They look equivalent to me.

How do I create a test to deep compare those two objects, order-independent in .Net 4.5.2, as that is a standard object implementation for us

I want to write tests like the one above. We are moving from Sybase ASE to SqlServer, and I want to assert that the Ase call and the SqlServer call are returning the same data, and I can't just add order to every sql call.

P.S> For political reasons I can't update from .net 4.5.2 to .net 8.* at this time


Solution

  • FluentAssertions was by far the easiest answer.

    It has a

    list<T>.ShouldBeEquivalentTo(List <T>) 
    

    assertion, that performs a deep compare, ignoring order, and on failure returns an error with exactly the fields and values that did not match.

    FluentAssertions v4.19.4 supports .Net 4.5.2

    Package Manager Console: NuGet\Install-Package FluentAssertions -Version 4.19.4

    Then wrote my Nunit test.

            [Test]
            public void Test_Int_GetSimpleObject_CrossDbDeep()
            {
                List<SimpleObject> rtnValSyb = dal.GetSimpleObject(bForceSybase);
                List<SimpleObject> rtnValMs = dal.GetSimpleObject(bForceMsSql);
    
                Assert.AreNotEqual(rtnValSyb.Count, 0);
                Assert.AreNotEqual(rtnValMs.Count, 0);
    
                //assert with deep compare ignoring order - EWB
                rtnValSyb.ShouldBeEquivalentTo(rtnValMs,"If this fails, the data is probably out of synch");
            }