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)
{
}
}
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"));
}
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.