views:

171

answers:

2

To accomplish this (but failing to do so) I'm reflecting over properties of an expected and actual object and making sure their values are equal. This works as expected as long as their properties are single objects, i.e. not lists, arrays, IEnumerable... If the property is a list of some sort, the test fails (on the Assert.AreEqual(...) inside the for loop).

public void WithCorrectModel<TModelType>(TModelType expected, string error = "") 
    where TModelType : class
{
    var actual = _result.ViewData.Model as TModelType;
    Assert.IsNotNull(actual, error);
    Assert.IsInstanceOfType(actual, typeof(TModelType), error);
    foreach (var prop in typeof(TModelType).GetProperties())
    {
        Assert.AreEqual(prop.GetValue(expected, null), prop.GetValue(actual, null), error);
    }
}

If dealing with a list property, I would get the expected results if I instead used CollectionAssert.AreEquivalent(...) but that requires me to cast to ICollection, which in turn requries me to know the type listed, which I don't (want to).
It also requires me to know which properties are list types, which I don't know how to.

So, how should I assert that two objects of an arbitrary type are equivalent?

Note: I specifically don't want to require them to be equal, since one comes from my tested object and one is built in my test class to have something to compare with.

+1  A: 

This works, not something I would really recommend but it works.

const string methodName = "Boolean SequenceEqual[TSource](System.Collections.Generic.IEnumerable`1[TSource], System.Collections.Generic.IEnumerable`1[TSource])";

var sequenceEqual = 
        typeof(Enumerable)
        .GetMethods()
        .First(m => m.ToString() == methodName);

foreach (var prop in typeof(TModelType).GetProperties())
{        
    var expectedValue = prop.GetValue(expected, null);
    var actualValue = prop.GetValue(actual, null);

    foreach (var item in a.GetType().GetInterfaces())
    {
        if (item.IsGenericType && item.Name.Contains("IEnumerable`1"))
        {
            Assert.IsTrue(
                (bool)sequenceEqual.MakeGenericMethod(
                    item.GetGenericArguments()[0]
                ).Invoke(null, new[] { expectedValue, actualValue })
            );              
        }
    }
}
ChaosPandion
I don't have the `.SequenceEqual()` method available. Do you know which assembly I need?
Tomas Lycken
You need C# 3 with a reference to System.Core.dll and `using System.Linq`.
SLaks
Hm... I'm in .NET 4 with a reference to System.Core.dll and `using System.Linq` - still no `.SequenceEqual()`...
Tomas Lycken
A: 

Not sure this will go down well, but one option would be to serialize the objects to a byte array and compare them. Of course this assumes that the objects you are dealing with are serializable.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

namespace NGTests
{
  class Program
  {
    static void Main(string[] args)
    {
      Person p1 = new Person() { FirstName = "Chris", LastName = "Taylor", Children = { new Person() { FirstName = "Tamrin", LastName = "Taylor" } } };
      Person p2 = new Person() { FirstName = "Chris", LastName = "Taylor", Children = { new Person() { FirstName = "Tamrin", LastName = "Taylor" } } };

      BinaryFormatter formatter = new BinaryFormatter();

      MemoryStream ms1 = new MemoryStream();
      formatter.Serialize(ms1, p1);

      MemoryStream ms2 = new MemoryStream();
      formatter.Serialize(ms2, p2);

      byte[] b1 = ms1.ToArray();
      byte[] b2 = ms2.ToArray();

      Console.WriteLine("Objects are Equal : {0}", b1.SequenceEqual(b2));

      Console.ReadKey();
    }
  }

  [Serializable]
  class Person
  {
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public List<Person> Children { get; private set; }

    public Person()
    {
      Children = new List<Person>();
    }
  }
}
Chris Taylor
Unfortunately, I can't assume that they'll be serializable, since I want to be able to do this with *any* type...
Tomas Lycken