More Unit Test Tricks
I've posted before about some tricks you can do to play with NUnit and get more out of your testing. Today I'm going to pass in some more tricks I came up with recently to use reflection to verify correctness.
Recently, we had a defect that involved the properties of a Control object that were lacking the appropriate categories. In the VS designer, they all showed up as "Misc". This is not a big deal and is fairly easy to fix, but it is also fairly easy for recidivation on this bug as it is easy to add a new property and forget to add a category.
So let's formally define a property that appears in the UI designer. A complete property in the UI designer is marked with a Browseable attribute and contains exactly one Category attribute.
Given that, we can write predicates for the conditions upon which our definition hinges:
private bool
IsBrowsable(object[]
attrs)
{
// returns true
if the set of attributes on a Type indicate browsable
foreach (object o
in attrs)
{
if (o is BrowsableAttribute)
{
BrowsableAttribute br = (BrowsableAttribute)o;
return br.Browsable;
}
}
return true;
}
In this case, we search the collection of attributes for a BrowseableAttribute. If we find one, we return its setting. Otherwise, without the attribute the property must be browseable so we return true by default.
private bool
IsCategorized(object[]
attrs)
{
// returns true
if a property has a category
foreach (object o
in attrs)
{
if (o is CategoryAttribute)
{
CategoryAttribute ca = (CategoryAttribute)o;
return ca.Category != null &&
ca.Category.Length >
0;
}
}
return false;
}
In this predicate, a property is considered categorized if it has a CategoryAttribute, the attribute's Category property is non-null and it's length is greater than zero.
Given that, we can use the following stub to test a UI object:
// get only the props declared by the final object (not base classes) that are
public
PropertyInfo[] props =
objectUnderScrutiny.GetType().GetProperties(BindingFlags.DeclaredOnly |
BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in props)
{
object[] attrs = prop.GetCustomAttributes(false);
if (!IsBrowsable(attrs))
{
//
skip if not browseable
continue;
}
if (!IsCategorized(attrs))
{
Assert.Fail("property
" + prop.Name +
" is missing
category.");
}
}
Lest I forget - there's a solid why this category of test is useful - with dispassionate calm, it will flag very human, future errors that are easy to make. When you implement a process, write code, etc. Be on the lookout "mandatory" steps that do not cause compile errors. Applying Category attributes is exactly such a step - without it, a control object would not be professional quality, but applying attributes is purely optional. It would be nice if you could flag in a base class rules for the existence of certain attributes in subclasses as well as to mark the error as fatal or non-fatal. This way an abstract class could not only dictate interface, but semantics as well.
In the meantime, unit tests will have to serve.