Using C# Anonymous Types to Assert Complex Results in Your Unit Tests
A developer gives a tutorial on how to perform unit testing on web applications using the C# language and anonymous types in his code.
Join the DZone community and get the full member experience.
Join For FreeDoes this sounds familiar to you:
You come to work early(ish) in the morning — ready to apply a new technology you've just learned about — just to find out that you can't. Your frustration grows as you understand that in order to use this "new and shiny" some adjustments must be made and what seemed so easy in last week training/conference/blog is in fact more complex in your project...
In my experience one of the reasons developers fail unit testing is the gap that exists between learning the basics of unit testing and TDD and applying it to your project — you have existing code to deal with, problematic dependencies, and on top of that most of the time you deal with complex data which you need to create and/or check at the end of your test.
Consider a "simple" customer
class:
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string TelephoneNumber { get; set; }
public DateTime Birthday { get; set; }
public string Occupation { get; set; }
public string Domain { get; set; }
public Address Address { get; set; }
}
That customer has many fields some of which I don't care about in all of my tests and on top of that it has nested classes — in this example an Address.
If you try to use Assert.AreEqual
in your unit testing of choice it would probably fail. This is caused by the fact that most unit testing frameworks that use the default equality functionality would only test if both the instances have the same reference. On top of that you would probably get a cryptic assertion failure:
TestProject1.RunMfgSiteSeqTest.SimpleEqual
Expected: <TestProject1.TestClasses.Customer>
But was: <TestProject1.TestClasses.Customer>
Obviously, there are solutions for this specific issue — you can override the equality operator (depending of your language of choice) or use a third-party assertion library such as FluentAssertions/Shouldly (C#) or Hamcrest (Java) or use the multiple assertions capability that was recently added to both NUnit and Java (or use mine) but all of those are tedious, and not always possible depending on your project. The issue becomes unbearable when you need to assert a collection of such objects with expected results.
I've wanted to find an easy way to check a list of objects — of any kind with a list of expected objects, only comparing some of the properties and get a good error message when assertion fails. The project was using C# (.NET 4.6) and so I've looked for a supported language construct that would auto-magically implement comparison and to string for me, after a bit of thinking I've decided to try using anonymous types.
Anonymous types were introduced to help create ad-hoc instances usually during Linq queries essentially by writing:
new { Id = 1234, Name = "Dror Helper" }
I would create a new type in my code that has two properties "Id" and "Name" and the cool thing is that I get Equals, HashCode, and ToString automatically implemented based on those properties. And so I was able to write the following test:
[Test]
public void GetAllCustomers_CustomersNameAndCountryAsExpectedSimple()
{
var result = GetAllCustomers();
var resultAsAnonymous = result.Select(customer =>
new {
customer.Name,
Address = new
{
customer.Address.Country
}
});
var expected = new[]
{
new {Name = "Nathan Salazar", Address = new {Country = "US"}},
new {Name = "Lani Decker", Address = new {Country = "UK"}},
new {Name = "Wade wrong", Address = new {Country = "France"}},
new {Name = "Sonia Page", Address = new {Country = "US"}},
};
CollectionAssert.AreEqual(expected, resultAsAnonymous);
}
In order to compare the actual result to my expected result, I needed to put the data I wanted to compare into another anonymous class and then use CollectionAssert.AreEqual
to compare the result to my expected data. And presto, I could compare my complex object to another object, and if (read: when) my test failed I could immediately understand why:
AssertionUsingAnonymousTypes.AssertEqualsTest.GetAllCustomers_CustomersNameAndCountryAsExpectedSimple
Expected is <<>f__AnonymousType0`2[System.String,<>f__AnonymousType1`1[System.String]][4]>, actual is <System.Linq.Enumerable+WhereSelectListIterator`2[AssertionUsingAnonymousTypes.TestClasses.Customer,<>f__AnonymousType0`2[System.String,<>f__AnonymousType1`1[System.String]]]>
Values differ at index [2]
Expected: <{ Name = Wade wrong, Address = { Country = France } }>
But using this method means that for each result I would need to create a "transformed" (resultAsAnonymous
) class with my data and make sure I compare the correct properties by name — which seemed error prone and quite frankly — too much work.
What I needed is to create a method that would handle the transformation:
- Receive two parameters: an enumerable of "real" classes and an enumerable of expected anonymous classes.
- Transform each item in the result to an anonymous type based on the expected type.
- Compare/assert.
At first it seemed impossible — how can I pass an anonymous type as a parameter, this type will be created by the compiler — and it looks something like this:
[CompilerGenerated]
[DebuggerDisplay("\\{ Name = {Name}, Address = {Address} }", Type = "<Anonymous Type>")]
internal sealed class \u003C\u003Ef__AnonymousType0<\u003CName\u003Ej__TPar, \u003CAddress\u003Ej__TPar>
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly \u003CName\u003Ej__TPar \u003CName\u003Ei__Field;
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private readonly \u003CAddress\u003Ej__TPar \u003CAddress\u003Ei__Field;
public \u003CName\u003Ej__TPar Name
{
get
{
return this.\u003CName\u003Ei__Field;
}
}
public \u003CAddress\u003Ej__TPar Address
{
get
{
return this.\u003CAddress\u003Ei__Field;
}
}
But then it hit me — I don't really need to know the type name as long as .NET does — which I can do using Generics:
public static void AssertEqualTo<TActual, TExpected>(this IEnumerable<TActual> actual, IEnumerable<TExpected> expectedAnonymous)
{
var expectedType = expectedAnonymous.First().GetType();
var transformedResult = new List<TExpected>();
foreach (var item in actual)
{
var actualType = item.GetType();
var result = (TExpected)TransformRealToAnonymous(item, actualType, expectedType);
transformedResult.Add(result);
}
CollectionAssert.AreEqual(expectedAnonymous, transformedResult);
}
I've wrote an extension method that excepts two parameters using the magic of generics and then convert each item from TActual
to TExpected
, I am assuming that both have properties with similar names and finally I use built in assertion to check the values.
The real magic happens in the TransformRealToAnontmous
method:
private static object TransformRealToAnonymous(object instance, Type realType, Type anonymousType)
{
var ctor = anonymousType.GetConstructors().Single();
var constructorParameters = ctor.GetParameters()
.Select(parameter => GetValue(instance, realType, parameter))
.ToArray();
return ctor.Invoke(constructorParameters);
}
private static object GetValue(object instance, Type instanceType, ParameterInfo parameterInfo)
{
var actualProperty = instanceType.GetProperty(parameterInfo.Name);
var actualPropertyValue = actualProperty.GetValue(instance);
if (!parameterInfo.ParameterType.IsAnonymousType())
{
return actualPropertyValue;
}
return TransformRealToAnonymous(actualPropertyValue, actualProperty.PropertyType, parameterInfo.ParameterType);
}
Basically, I'm counting on another aspect of C# anonymous types — they have a constructor that accepts a list of parameters ordered and named according to the property names:
[DebuggerHidden]
public \u003C\u003Ef__AnonymousType0(
\u003CName\u003Ej__TPar Name,
\u003CAddress\u003Ej__TPar Address)
{
// ISSUE: reference to a compiler-generated field
this.\u003CName\u003Ei__Field = Name;
// ISSUE: reference to a compiler-generated field
this.\u003CAddress\u003Ei__Field = Address;
}
And so I was able to "grab" the value from the real class and pass it to the constructor of the new anonymous class. While the code is confusing and might require more than one reading to understand — the end result is simple to use:
[Test]
public void GetAllCustomers_CustomersNameAndCountryAsExpected()
{
var result = GetAllCustomers();
var expected = new[]
{
new {Name = "Nathan Salazar", Address = new {Country = "US"}},
new {Name = "Lani Decker", Address = new {Country = "UK"}},
new {Name = "Wade wrong", Address = new {Country = "France"}},
new {Name = "Sonia Page", Address = new {Country = "US"}},
};
result.AssertEqualTo(expected);
}
Create an anonymous type with the same property names of the real class you want to compare and that's it.
If you want to try it out the code is available on GitHub for your debugging pleasure.
I hope this trick would be useful to you as it was to me. Try it in your next unit testing project and hopefully it will remove one of the hurdles each developer attempting unit testing must face.
And until then, happy coding!
Published at DZone with permission of Dror Helper, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments