(Deep) comparison of an object to a reference in unit tests (C#)
Asked Answered
C

4

6

In a Unit Test (in Visual Studio 2008) I want to compare the content of a large object (a list of custom types, to be precise) with a stored reference of this object. The goal is to make sure, that any later refactorings of the code produces the same object content.

Discarded Idea: A first thought was to serialize to XML, and then compare the hardcoded strings or a file content. This would allow for easy finding of any difference. However since my types are not XML serializable without a hack, I must find another solution. I could use binary serialization but this will not be readable anymore.

Is there a simple and elegant solution to this?

EDIT: According to Marc Gravell's proposal I do now like this:

using (MemoryStream stream = new MemoryStream())
        {
            //create actual graph using only comparable properties
            List<NavigationResult> comparableActual = (from item in sparsed
                                                       select new NavigationResult
                                                       {
                                                           Direction = item.Direction,
                                                           /*...*/
                                                           VersionIndication = item.VersionIndication
                                                       }).ToList();

            (new BinaryFormatter()).Serialize(stream, comparableActual);
            string base64encodedActual = System.Convert.ToBase64String(stream.GetBuffer(), 0, (int)stream.Length);//base64 encoded binary representation of this                
            string base64encodedReference = @"AAEAAAD....";//this reference is the expected value
            Assert.AreEqual(base64encodedReference, base64encodedActual, "The comparable part of the sparsed set is not equal to the reference.");
        }

In essence I do select the comparable properties first, then encode the graph, then compare it to a similarly encoded reference. Encoding enables deep comparison in a simple way. The reason I use base64 encoding is, that I can easily store the reference it in a string variable.

Canton answered 13/8, 2010 at 10:0 Comment(2)
The serialization idea does seem a good one for deep graphs.Estaestablish
Serialization is not completely reliable. You could get false negatives. See #2244723Oldham
T
5

I would still be inclined to use serialization. But rather than having to know the binary, just create an expected graph, serialize that. Now serialize the actual graph and compare bytes. This is only useful to tell you that there is a difference; you'd need inspection to find what, which is a pain.

Tribunate answered 13/8, 2010 at 10:5 Comment(3)
Yeah, I'm currently doing this like you say, but I want to avoid the pain!Canton
So now, still do I feel the pain, but my test now works with the edit in the question, doing it like you proposed.Canton
Maybe a combination of binary serialization and deep reflection analysis (as in my idea) in case of mismatch in binary comparision will solve it.Lorri
L
1

I would use the hack to do XML comparision. Or you could use reflection to automaticaly traverse object properties (but this will traverse ALL of them, also some you could not want to).

Lorri answered 13/8, 2010 at 10:7 Comment(2)
Unfortunately the hack is, to manipulate a generated file (generated by the entity framework)Canton
Isn't entity framework generated classes partial? Can't you apply the "hack" in another file extending the partial classes as needed? If you are using VS2010 it also includes ADO.NET EntityObject Generator template. Then you can actually modify Entity Objects template to match your requirements.Clarettaclarette
M
1

I would make each custom type inherit IComparable, and provide equality methods, that compare each custom types, as well as making the main class ICompareble, You can then simply compare the 2 objects ( if you have them in memory when running unit tests) If not then I would suggest either serializing, or defining constants which you expect the refactored object to have.

Mochun answered 13/8, 2010 at 10:12 Comment(1)
Well, it's a nice solution too as it add some functionality to classes at cost of writing code and possible leaks (if some object don't provide IComparable, then it will NOT be reliable).Lorri
H
0

I can think of 2 options not mentioned (yet) to avoid using Serialization:

  1. You could create a HashCode using something like SHA256.HashData and convert the result to a Base64 encoded string and store that as a const in your unit test:

     var hashBytes = SHA256.HashData(data);
     var base64EncodedString = Convert.ToBase64String(hashBytes);
    
  2. You could create an expected object graph and then use FluentAssertions nuget package to traverse the object graph. I use this frequently to do the same and find it has always worked well. FluentAssertions is pretty robust!

    var expectedModel = //create your expected object graph here
    actual.Should().BeEquivalentTo(expectedModel, options => options.WithStrictOrdering());
    

See:

Hardhearted answered 5/7, 2024 at 14:28 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.