Tuesday, 6 October 2015

Using the TestCaseSource attribute in NUnit

I’ve used NUnit for the last few years but I only recently discovered the TestCaseSource attribute which is very useful in certain circumstances.

The scenario

Imagine you have a DeviceInformation class. It’s job is to take a user agent, parse it and provide information about the device. The class could look something like this:
public class DeviceInformation
{
    public bool IsDesktop { get; private set; }
    public bool IsMobile { get; private set; }
    public bool IsTablet { get; private set; }
    public string Manufacturer { get; private set; }

    public DeviceInformation(string userAgent)
    {
        // Some code here which sets the class properties
    }
}
One way to attack this is to create a bunch of user agents and use the TestCase attribute to confirm that multiple user agents return the same result.
private const string USER_AGENT_IPHONE_4 = "...";
private const string USER_AGENT_GALAXY_S5 = "...";
private const string USER_AGENT_LG_G4 = "...";
private const string USER_AGENT_GALAXY_TAB = "...";

[TestCase(USER_AGENT_IPHONE_4)]
[TestCase(USER_AGENT_GALAXY_S5)]
[TestCase(USER_AGENT_LG_G4)]
public void IsMobileDevice_UserAgentIsMobile_ReturnsTrue(string userAgent)
{
    Assert.IsTrue(new DeviceInformation(userAgent).IsMobile);
}

[TestCase(USER_AGENT_GALAXY_TAB)]
[TestCase(USER_AGENT_GALAXY_S5)]
[TestCase(USER_AGENT_LG_G4)]
public void IsAndroidDevice_UserAgentIsAndroid_ReturnsAndroid(string userAgent)
 {
     Assert.That(new DeviceInformation(userAgent).Manufacturer, Is.EqualTo("Android"));
 }

// More tests to cover the IsDesktop, IsTablet and Manufacturer properties here
This is a trivial example and there are already quite a few user agents. You can see that if I were to thoroughly unit test DeviceInformation I would need a lot more user agents and a lot more tests. As the number of users agents increased the number of attributes on each test would increase and the tests would become more difficult to maintain. Each time a user agent was added I’d need to scan every single test and make sure new attributes were added where appropriate.

Using the TestCaseSource attribute

From the NUnit documentation:
TestCaseSourceAttribute is used on a parameterized test method to identify the property, method or field that will provide the required arguments.
This means you can use a property, method or field to hold all of the user agent strings for a particular test rather than specify each one in it’s own attribute. Here I’ve used arrays to organise user agents into groups:
readonly string[] mobileUserAgents =
{
    USER_AGENT_IPHONE_4,
    USER_AGENT_GALAXY_S5,
    USER_AGENT_LG_G4
};

readonly string[] androidUserAgents =
{
    USER_AGENT_GALAXY_TAB,
    USER_AGENT_GALAXY_S5,
    USER_AGENT_LG_G4
};

[Test, TestCaseSource("mobileUserAgents")]
public void IsMobileDevice_UserAgentIsMobile_ReturnsTrue(string userAgent)
{
    Assert.IsTrue(new DeviceInformation(userAgent).IsMobile);
}

[Test, TestCaseSource("androidUserAgents")]
public void IsAndroidDevice_UserAgentIsAndroid_ReturnsAndroid(string userAgent)
{
    Assert.That(new DeviceInformation(userAgent).Manufacturer, Is.EqualTo("Android"));
}
Now each test only has one attribute, much better. Also, to keep the snippet short I’ve only added two groups of user agents, but for a thorough test I’d add a lot more. For example appleUserAgents, tabletUserAgents, desktopUserAgents, backBerryUserAgents, windowsPhoneUserAgents etc. Now when you want to test a new user agent you simply create a new user agent variable and add it to the appropriate arrays. The is no need to change individual tests. Easy.

No comments:

Post a comment