bi2-ma
Last Updated: February 25, 2016
·
2.352K
· keboo

Exploiting NUnit attributes (ValuesAttribute)

See my other Pro Tips for an example of using NUnit’s ValueSourceAttribute. This post offers another example of how to extend an NUnit attribute to get better unit tests.

For this example NUnit version 2.5 or greater is required.

Consider the following enum and test object.

public enum AnimalEnum : byte
{
    None,
    Dog,
    Cat
}
public class ObjectToTest
{    
    public object GetValueFromEnum(AnimalEnum enumValue)
    {
        switch(enumValue)
        {
            case AnimalEnum.None:
                return new NullAnimal();
            case AnimalEnum.Dog:
                return new Dog();
            case AnimalEnum.Cat:
                return new Cat();
            default:
                throw new InvalidOperationException(string.Format("{0} is not supported", enumValue));
        }
    }
}

We might want to write a test to ensure that all values in the enum return an object. That way, any programmer who modifies the enum will need to also modify the GetValueFromEnum method.

There are a few ways to write the test:

  • Use the NUnit ValuesAttribute and specify all of the values in the enum. This will not catch the case where a programmer adds to the enumeration and forgets to update the GetValueFromEnum method.
  • Use the Enum.GetValues(Type) method within the test, and loop through all values. This will work, but I would argue there is a cleaner way to do it.
  • Extend the NUnit ValuesAttribute to use all of the values in the enum.

Both the second and third options will work, so lets try both. The second option would look something like this:

[TestFixture]
public class TestFixture 
{
    [Test]
    public void Test_AllEnumValuesReturnAnimal()
    {
        var objectUnderTest = new ObjectToTest();
        var enumAnimals = Enum.GetValues(typeof(AnimalEnum)).Cast<AnimalEnum>();
        foreach(AnimalEnum enumValue in enumAnimals)
        {
            Assert.IsNotNull(objectUnderTest.GetValueFromEnum(enumValue));
        }
    }
}

A perfectly valid test that should do what we want, but I would argue there is a much cleaner way to do this.

Lets extend the NUnit ValuesAttribute.

public class AllEnumValuesAttribute : ValuesAttribute
{
    public AllEnumValuesAttribute(Type enumType)
    {
        if (enumType.IsEnum == false)
        {
            throw new InvalidOperationException(string.Format("{0} must be an enum type", enumType.Name));
        }
        data = Enum.GetValues(enumType).Cast<object>().ToArray();
    }
}

The ValuesAttribute has a protected object[] called data. NUnit uses this array when building up each test to run.

This changes our unit test to look like:

[TestFixture]
public class TestFixture 
{
    [Test]
    public void Test_AllEnumValuesReturnAnimal([AllEnumValues(typeof(AnimalEnum))] AnimalEnum enumValue)
    {
        var objectUnderTest = new ObjectToTest();
        Assert.IsNotNull(objectUnderTest.GetValueFromEnum(enumValue));
    }
}

IMO this is a much cleaner unit test, but there is another advantage of using the ValuesAttribute. Depending on the test runner, each enum value will appear as its own unit test (The test runner that comes with the NUnit framework will do this). So the above unit test will actually display as three unit tests, since the enum has three values.

Source: http://dotnetgeek.tumblr.com/post/2893620033/exploiting-nunit-attributes-valuesattribute

Say Thanks
Respond