kqxhow
Last Updated: February 25, 2016
·
1.909K
· keboo

Exploiting NUnit attributes (ValueSourceAttribute)

Often times it become necessary to use reflection in a unit test to verify that all child classes pass a particular test.

There are three basic ways to handle this:

  • Don’t write the test, code with no tests is always bug-free right?
  • Create an array of all of the child types. This is easy to do but not very scalable if the object is likely to be extended many times.
  • Use reflection to find all of the child types.

This post is not going to be a lesson in reflection, rather I plan to show a simple method for abstracting the reflection away from the unit test into a re-usable attribute.

For this example NUnit version 2.5 or greater is required.

For reference here is the object to test. (Yes I understand that there are software patterns that will avoid writing methods like the one I present below, but this is a post for improving unit tests not following software patterns).

public class ObjectToTest
{
    public bool TestObject(AbstractClass class)
    {
        if (class == null) throw new ArgumentNullException("class");

        if (class is Implementation1)
        {
            //Do something
            return true;
        }
        if (class is Implementation2)
        {
            //Do something else
            return true;
        }
        throw new InvalidOperationException(string.Format("{0} is not supported", class.GetType().Name));
    }
}

public abstract class AbstractClass
{ }

public class Implementation1 : AbstractClass
{ }

public class Implementation2 : AbstractClass
{ }

Now we need a test fixture to test our object. Using the ValueSourceAttribute we can write a unit test that uses all of the child types of AbstractClass.

[TestFixture]
public class TestFixture 
{
    [Test]
    public void Test_ChildClassesCanBeTested([ValueSource("getSubTypeObjects")] AbstractClass class)
    {
        var objectUnderTest = new ObjectToTest();
        Assert.IsTrue(objectUnderTest.TestObject(class));
    }

    public static IEnumerable<AbstractClass> GetSubTypeObjects()
    {
        var objectType = typeof (AbstractClass);
        var childTypes = new List<AbstractClass>();
        Assembly assembly = objectType.Assembly;

        foreach (Type type in assembly.GetTypes())
        {
            if (type.IsAbstract || type.IsClass == false)
                continue;
            if (type.IsSubclassOf(objectType))
            {
                childTypes.Add((AbstractClass)assembly.CreateInstance(type.FullName));
            }
        }
        return childTypes.ToArray();
    }
}

This test will execute once for each child class of AbstractClass that it finds.

Now what if we want to use this same idea in other test fixtures? It would not take much work to move GetSubTypeObjects() to some static class and make it generic, but the string constructor parameter in the ValueSourceAttribute would need to be kept in sync with this other class if it were ever modified. Hard coded strings always make me nervous even if they are only in unit tests.

My approach is to extend the attribute and keep the hard coded string with the field it references. We can do exactly that by creating our own attribute and extending ValueSourceAttribute.

public class AllSubTypesAttribute : ValueSourceAttribute
{
    private static IEnumerable<object> _values;

    public AllSubTypesAttribute(Type baseType) 
        : base(typeof(AllSubTypesAttribute), "_values")
    {
        _values = getSubTypeObjects(baseType);
    }

    private static IEnumerable<object> getSubTypeObjects(Type type)
    {
        var childTypes = new List<object>();
        Assembly assembly = type.Assembly;

        foreach (Type assemblyType in assembly.GetTypes())
        {
            if (assemblyType.IsAbstract || assemblyType.IsClass == false)
                continue;
            if (assemblyType.IsSubclassOf(type))
            {
                childTypes.Add(assembly.CreateInstance(assemblyType.FullName));
            }
        }
        return childTypes.ToArray();
    }
}

Because ValueSourceAttribute’s constructor takes in a string for the member that it should use it is impossible to get away from using it. What we can do is have the string reference a field within the attribute. This effectively hides the reflection and the name of the member being used from the unit test that is using this attribute.

Please note that this attribute assumes that all sub types are int he same assembly as the given type. This may be a problem if you are wanting to find all of the classes that extend say System.Windows.Forms.UserControl. For my particular application I am only concerned with sub classes that are within my own assembly. We could easily overload the constructor to take in an Assembly in addition to the type rather then inferring it from the type.

We can now change our previous unit test to:

[TestFixture]
public class TestFixture 
{
    [Test]
    public void Test_ChildClassesCanBeTested([AllSubTypes(typeof(AbstractClass))] AbstractClass class)
    {
        var objectUnderTest = new ObjectToTest();
        Assert.IsTrue(objectUnderTest.TestObject(class));
    }
}

Much cleaner then the previous implementation and easily re-usable in other test fixtures. This test will run once for each sub class of AbstractClass that it finds.

The reflection logic within the attribute will work for basic object hierarchies but more advanced logic is required to handle things such as the base type being an interface.

Source: http://dotnetgeek.tumblr.com/post/2851360238/exploiting-nunit-attributes-valuesourceattribute

Say Thanks
Respond