unit test

Testing objects for equality or similarity

There are times when you need to test the equality or similarity of two objects. Most of the times you encounter this need while unit testing, when you craft some input data, call a method to do some work on it, and assert on the result.

Presenting Deep Equal https://github.com/jamesfoster/DeepEqual

Deep equal does exactly what it says it does, it compares objects for equality, while supporting nested objects, skipping specific properties, skipping extra properties, and being open for extensibility (via implementing its IComparison interface).

Checking for equality

var a = new { SomeProperty = "some property value" };
var b = new { SomeProperty = "some property value" };

var areEqual = a.IsDeepEqual(b);

The areEqual variable will hold true, as both a and b have only one property, and the value of that property is the same for both.

Ignoring extra properties

var a = new { SomeProperty = "some property value"};
var b = new{SomeProperty = "some property value", SomeOtherProperty = "some other property value"};
var areEqual = a.IsDeepEqual(b);

The areEqual variable will hold false, as b has an extra property.

You can configure DeepEqual to ignore extra properties by calling

a.WithDeepEqual(b).IgnoreUnmatchedProperties().Compare();

Ignoring specific properties

Moreover, you can decide to ignore a property with one/both of the following constructs

a.WithDeepEqual(b).IgnoreSourceProperty(x => x.SomeProperty).Assert();
a.WithDeepEqual(b).IgnoreDestinationProperty(x => x.SomeProperty).Assert();

Custom comparisons

DeepEqual can be extended by implementing IComparison

Here’s an example where case insensitive ordinal string comparison is desired for all string types.

class CustomStringComparison : IComparison
{
private readonly StringComparison _comparisonType;

public CustomStringComparison(StringComparison comparisonType)
{
  _comparisonType = comparisonType;
}

public bool CanCompare(Type type1, Type type2)
{
  return type1 == typeof(string) && type2 == typeof(string);
}

public ComparisonResult Compare(IComparisonContext context, object value1, object value2)
{
  var value1Str = (string)value1;
  var value2Str = (string)value2;

  if (string.Equals(value1Str, value2Str, _comparisonType))
    return ComparisonResult.Pass;

  context.AddDifference(new Difference
  {
    Breadcrumb = context.Breadcrumb,
    Value1 = value1,
    Value2 = value2
  });

  return ComparisonResult.Fail;
  }
}

var a = new { SomeProperty = "some property value" };
var b = new { SomeProperty = "SOME property value" };

a.WithDeepEqual(b).WithCustomComparison(new CustomStringComparison(StringComparison.OrdinalIgnoreCase)).Assert();

In the above scenario all the string values will be piped in the CustomStringComparison which in turn will perform the configured string comparison. However,  sometimes you may want to employ a different comparison behavior based on the actual property you are checking, for instance all the properties named “MyProperty” should be checked in a case sensitive manner, while for all the others the comparison should be case insensitive. You can achieve this by checking the IComparisonContext.Breadcrumb property, which depicts the path to the currently compared value.

Moreover, if your IComparison implementation cannot tell if two values are equal, it can just return ComparisonResult.Inconclusive and DeepEqual will continue probing the other registered IComparison implementations (including its own).