In C# objects can be compared with the == operator, with the Equals(Object) member, with the Object. Equals(Object, Object) method or using custom comparators that implement one of or more of the IEquatable<T> , IComparable , IStructuralEquatable or IStructuralComparable interfaces. There's also a Object.
NUnit is able to compare single-dimensioned arrays, multi-dimensioned arrays, nested arrays (arrays of arrays) and collections. Two arrays or collections are considered equal if they have the same dimensions and if each pair of corresponding elements is equal.
An EqualConstraint is used to test whether an actual value is equal to the expected value supplied in its constructor, optionally within a specified tolerance.
This can occur through simple assignment, as shown in the following example. Value equality means that two objects contain the same value or values. For primitive value types such as int or bool, tests for value equality are straightforward.
Do not override Equals just for testing purposes. It's tedious and affects domain logic. Instead,
No additional logic on your objects. No extra tasks for testing.
Just use this simple method:
public static void AreEqualByJson(object expected, object actual)
{
var serializer = new System.Web.Script.Serialization.JavaScriptSerializer();
var expectedJson = serializer.Serialize(expected);
var actualJson = serializer.Serialize(actual);
Assert.AreEqual(expectedJson, actualJson);
}
It seems to work out great. The test runner results info will show the JSON string comparison (the object graph) included so you see directly what's wrong.
Also note! If you have bigger complex objects and just want to compare parts of them you can (use LINQ for sequence data) create anonymous objects to use with above method.
public void SomeTest()
{
var expect = new { PropA = 12, PropB = 14 };
var sut = loc.Resolve<SomeSvc>();
var bigObjectResult = sut.Execute(); // This will return a big object with loads of properties
AssExt.AreEqualByJson(expect, new { bigObjectResult.PropA, bigObjectResult.PropB });
}
If you can't override Equals for any reason, you can build a helper method that iterates through public properties by reflection and assert each property. Something like this:
public static class AssertEx
{
public static void PropertyValuesAreEquals(object actual, object expected)
{
PropertyInfo[] properties = expected.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
object expectedValue = property.GetValue(expected, null);
object actualValue = property.GetValue(actual, null);
if (actualValue is IList)
AssertListsAreEquals(property, (IList)actualValue, (IList)expectedValue);
else if (!Equals(expectedValue, actualValue))
Assert.Fail("Property {0}.{1} does not match. Expected: {2} but was: {3}", property.DeclaringType.Name, property.Name, expectedValue, actualValue);
}
}
private static void AssertListsAreEquals(PropertyInfo property, IList actualList, IList expectedList)
{
if (actualList.Count != expectedList.Count)
Assert.Fail("Property {0}.{1} does not match. Expected IList containing {2} elements but was IList containing {3} elements", property.PropertyType.Name, property.Name, expectedList.Count, actualList.Count);
for (int i = 0; i < actualList.Count; i++)
if (!Equals(actualList[i], expectedList[i]))
Assert.Fail("Property {0}.{1} does not match. Expected IList with element {1} equals to {2} but was IList with element {1} equals to {3}", property.PropertyType.Name, property.Name, expectedList[i], actualList[i]);
}
}
Try FluentAssertions library:
dto.Should().BeEquivalentTo(customer)
It can also be installed using NuGet.
Override .Equals for your object and in the unit test you can then simply do this:
Assert.AreEqual(LeftObject, RightObject);
Of course, this might mean you just move all the individual comparisons to the .Equals method, but it would allow you to reuse that implementation for multiple tests, and probably makes sense to have if objects should be able to compare themselves with siblings anyway.
I prefer not to override Equals just to enable testing. Don't forget that if you do override Equals you really should override GetHashCode also or you may get unexpected results if you are using your objects in a dictionary for example.
I do like the reflection approach above as it caters for the addition of properties in the future.
For a quick and simple solution however its often easiest to either create a helper method that tests if the objects are equal, or implement IEqualityComparer on a class you keep private to your tests. When using IEqualityComparer solution you dont need to bother with the implementation of GetHashCode. For example:
// Sample class. This would be in your main assembly.
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// Unit tests
[TestFixture]
public class PersonTests
{
private class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null && y == null)
{
return true;
}
if (x == null || y == null)
{
return false;
}
return (x.Name == y.Name) && (x.Age == y.Age);
}
public int GetHashCode(Person obj)
{
throw new NotImplementedException();
}
}
[Test]
public void Test_PersonComparer()
{
Person p1 = new Person { Name = "Tom", Age = 20 }; // Control data
Person p2 = new Person { Name = "Tom", Age = 20 }; // Same as control
Person p3 = new Person { Name = "Tom", Age = 30 }; // Different age
Person p4 = new Person { Name = "Bob", Age = 20 }; // Different name.
Assert.IsTrue(new PersonComparer().Equals(p1, p2), "People have same values");
Assert.IsFalse(new PersonComparer().Equals(p1, p3), "People have different ages.");
Assert.IsFalse(new PersonComparer().Equals(p1, p4), "People have different names.");
}
}
I've tried several approaches mentioned here. Most involve serializing your objects and doing a string compare. While super easy and generally very effective, I've found it comes up a little short when you have a failure and something like this gets reported:
Expected string length 2326 but was 2342. Strings differ at index 1729.
Figuring out where where the differences are is a pain to say the least.
With FluentAssertions' object graph comparisons (i.e. a.ShouldBeEquivalentTo(b)
), you get this back:
Expected property Name to be "Foo" but found "Bar"
That's much nicer. Get FluentAssertions now, you'll be glad later (and if you upvote this, please also upvote dkl's answer where FluentAssertions was first suggested).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With