Agile .NET

Ideas & Gotchas

TDD – Test similar objects automatically

Posted by Vlad on September 18, 2007


When testing you get to spend lots of time writing the setup and validation parts of the unit test. I talked in this post about a way to speed up the setup phase – generate objects that have random data in their value-type properties.

Something similar can be applied to the validation part. Let’s take an example of a service implementation with WCF Service Factory. Normally you receive a request containing Data Contract objects, you translate those into Business Entities and pass them to a Business Action. This may return a result also expressed as Business Entities that the service implementation must translate into Data Contract objects and forward them to the client.

Luckily, you have a recipe for creating entity translators. But if you want to test the service implementation thoroughly you have to make sure that every property gets properly translated and forwarded from Data Contracts to Business Entities and back. This means lots of “Assert.AreEqual(source.MyProperty, destination.MyProperty)”. More, if you add or remove properties you have to remember to add them to the asserts list, or they will not be tested.

One way to do this automatically (at least for the value-type properties) is to compare the properties that have the same name and type in both objects – taking advantage of the fact that most of the time they are identical. The following method accomplishes this for non-array value or string properties. It can be extended for arrays and indexer properties or even to drill down into reference types, but i find it difficult to follow what exactly gets tested or not. It can also be used for objects of the same type as a quick property-by-property equality check (or you can use it inside an overridden Equals(), but you have to remove the “obj1.Equals(obj2)” from the first if).

public static void EqualByPropertyNames(object obj1, object obj2, params string[] excludeProperties)

{

if (obj1 == obj2 || obj1.Equals(obj2))

return;

Assert.IsNotNull(obj1);

Assert.IsNotNull(obj2);

Type obj1Type = obj1.GetType();

Type obj2Type = obj2.GetType();

PropertyInfo[] properties1 = obj1Type.GetProperties();

List<string> excludedList = new List<string>(excludeProperties);

List<PropertyInfo> properties2 = new List<PropertyInfo>(obj2Type.GetProperties());

foreach (PropertyInfo prop in properties1)

{

if (prop.CanRead &&

(prop.PropertyType.IsValueType || prop.PropertyType == typeof(System.String))

&& !excludedList.Contains(prop.Name)

&& prop.GetIndexParameters().Length == 0)

{

PropertyInfo prop2 = properties2.Find(delegate(PropertyInfo target)

{ return target.Name == prop.Name && target.PropertyType == prop.PropertyType; });

if (prop2 != null)

Assert.AreEqual(prop.GetValue(obj1, null), prop2.GetValue(obj2, null), “Mismatch in objects {1} and {2} – property {0}.”, prop.Name, obj1Type, obj2Type);

}

}

}

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>