Creating a EqualityComparer on the fly with Lambdas

When I’m writing unit tests I need the code to be a succinct as possible and one of the issues I’ve had is comparing objects sets/graphs to confirm expectations. Here’s a couple of the unit tests that test my solution and hopefully demonstrate what I’m after :

public class TestObject
{
   public string FirstProperty { get; set; }
   public string SecondProperty { get; set; }
}

[TestMethod]
public void EqualityCompareFactory_Should_DetectEquality_Given_TwoObjectInstancesWithTheSamePropertyValues()
{
  var comparer = EqualityComparerFactory<TestObject>.Create((x, y) => x.FirstProperty == y.FirstProperty);
  Assert.IsTrue(comparer.Equals(new TestObject { FirstProperty = "first" }, new TestObject { FirstProperty = "first"}));
}

[TestMethod]
public void EqualityCompareFactory_Should_DetectInequality_Given_TwoObjectInstancesWithTheDifferentPropertyValues()
{
  var comparer = EqualityComparerFactory<TestObject>.Create((x, y) => x.FirstProperty == y.FirstProperty);
  Assert.IsFalse(comparer.Equals(new TestObject { FirstProperty = "first" }, new TestObject { FirstProperty = "different" }));
}

The key here is that, rather than having to create a derived class of EqualityComparer<> I can simply use a factory and pass it a lambda that defines the comparison operation. In my business rule testing I can now write something like the following assertion :

var testResults = TimesheetItemBL.TestReturnSavePaymentFlags.ToList();
var expected = new List<TimesheetItemPaymentFlags>{
     new TimesheetItemPaymentFlags { AppliedRule = null, Date = testWeekStartDate, IsPaid = null },
     new TimesheetItemPaymentFlags { AppliedRule = AbsenceRules.WaitingDay, Date = testWeekStartDate.AddDays(1), IsPaid = false },
     new TimesheetItemPaymentFlags { AppliedRule = AbsenceRules.WaitingDay, Date = testWeekStartDate.AddDays(2), IsPaid = false },
     new TimesheetItemPaymentFlags { AppliedRule = AbsenceRules.WaitingDay, Date = testWeekStartDate.AddDays(3), IsPaid = false },
     new TimesheetItemPaymentFlags { AppliedRule = AbsenceRules.WaitingDay, Date = testWeekStartDate.AddDays(4), IsPaid = false },
     new TimesheetItemPaymentFlags { AppliedRule = AbsenceRules.WaitingDay, Date = testWeekStartDate.AddDays(5), IsPaid = false },
     new TimesheetItemPaymentFlags { AppliedRule = AbsenceRules.Paid, Date = testWeekStartDate.AddDays(6), IsPaid = true },
 };
NUnit.Framework.Assert.That(testResults, new CollectionEquivalentConstraint(expected).Using<TimesheetItemPaymentFlags>(
     EqualityComparerFactory<TimesheetItemPaymentFlags>.Create((x, y) => x.AppliedRule == y.AppliedRule && x.Date.Equals(y.Date) && x.IsPaid == y.IsPaid, x => x.Date.GetHashCode())
 ));

Admittedly this is still pretty verbose and I might extract some helper functions to reduce the size of this but it does demonstrate the simplicity of the solution which, by the way, looks like this :

public class DynamicEqualityComparer<T> : EqualityComparer<T>
{
   private readonly Func<T, T, bool> _equalsFunction;
   private readonly Func<T, int> _hashCodeFunction;

   internal DynamicEqualityComparer(Func<T, T, bool> equalsFunction, Func<T, int> hashCodeFunction)
   {
       _equalsFunction = equalsFunction;
       _hashCodeFunction = hashCodeFunction;
   }

   public override bool Equals(T x, T y)
   {
       return _equalsFunction(x, y);
   }

   public override int GetHashCode(T obj)
   {
       return _hashCodeFunction(obj);
   }
}

public class EqualityComparerFactory<T> where T : class
{
   public static DynamicEqualityComparer<T> Create(Func<T, T, bool> equalsFunction)
   {
       return new DynamicEqualityComparer<T>(equalsFunction, x => x.GetHashCode());
   }

   public static DynamicEqualityComparer<T> Create(Func<T, T, bool> equalsFunction, Func<T, int> hashCodeFunction)
   {
       return new DynamicEqualityComparer<T>(equalsFunction, hashCodeFunction);
   }
}

There’s some complexity involved in providing a hasCode function that is meaningful to the comparison, but otherwise it’s fairly basic.

Testing Internals

There’s a very handy attribute called InternalsVisibleTo that can be used in a class to make internally declared members accessible to a specific assembly. This probably isn’t wise for most situations but it’s very handy for testing internals from a separate test assembly. It’s also handy to provide a parameterised constructor that can accept different dependencies whilst the default constructor is hard-wired to the standard dependencies – this seems like an easy way to add DI for the purposes of testing to existing applications without disturbing the app structure too much or introducing a confusing DI factory.

It’s a bit of a code smell adding code simply for testing purposes and in particular following different paths through the code during testing whilst production code follows another path (default constructor in production, parameterised in test) however, I think it’s a reasonable compromise to take if it means adding valuable tests without too much extra work!

image