Revisiting Data Driven Unit Tests With MSTest

Posted by AgileCoder on June 6, 2019

One of my most popular posts here is Data Driven MSTest Unit Tests With Inline Data. I posted it in early 2015, and back then it was incredibly useful and relevant. I get links from google and stack overflow to it every day.

Unfortunately it is horribly obsolete.

It’s time to revisit data-driven parameterized tests using the Microsoft Testing framework.

I’m going to start with the same extension method I used before. This method is used to determine the proper Quarter of the Year for a given Date.

public static class Extensions
{
	/// <summary>
	/// Get the Qtr number where this date falls under.
	/// </summary>
	/// <param name="parmDate">Date to get quarter for</param>
	/// <param name="offset">offset to add/subtract from quarter</param>
	/// <returns>Qtr Number</returns>
	public static int GetQuarterNumber(this DateTime parmDate, int offset = 0)
	{
		return (int)Math.Ceiling(parmDate.AddMonths(offset * 3).Month / 3m);
	}
}

The old way…

What I gave as an option before was to use Anonymous Types and LINQ with an Anonymous function like this:

[TestMethod]
public void MonthReturnsProperQuarter()
{
	// Arrange
	var values = new[] {
		new { inputDate = new DateTime(2013, 1, 1), expectedQuarter = 1},
		new { inputDate = new DateTime(2013, 2, 1), expectedQuarter = 1},
		new { inputDate = new DateTime(2013, 3, 1), expectedQuarter = 1},
		new { inputDate = new DateTime(2013, 4, 1), expectedQuarter = 2},
		new { inputDate = new DateTime(2013, 5, 1), expectedQuarter = 2},
		new { inputDate = new DateTime(2013, 6, 1), expectedQuarter = 2},
		new { inputDate = new DateTime(2013, 7, 1), expectedQuarter = 3},
		new { inputDate = new DateTime(2013, 8, 1), expectedQuarter = 3},
		new { inputDate = new DateTime(2013, 9, 1), expectedQuarter = 3},
		new { inputDate = new DateTime(2013, 10, 1), expectedQuarter = 4},
		new { inputDate = new DateTime(2013, 11, 1), expectedQuarter = 5},
		new { inputDate = new DateTime(2013, 12, 1), expectedQuarter = 4}
	};
	values.ToList().ForEach(val =>
		{
			// Act
			int actualQuarter = val.inputDate.GetQuarterNumber();
			// Assert
			Assert.AreEqual(val.expectedQuarter, actualQuarter,
				"Failed for inputDate={0} and expectedQuarter={1}.", val.inputDate, val.expectedQuarter);
		});
}

The new way…

With the release of Visual Studio 2017 the Microsoft.VisualStudio.TestTools.UnitTesting namespace added the DataRowAttribute class.

You can now parameterize your tests in a manner very similar to that supported by NUnit and xUnit. You add the input parameters to your test and use the [DataRow()] attribute to list in incoming test and expected values, like this:

[TestClass]
public class ExtensionTests
{
    [TestMethod]
    [DataRow(1,1)]
    [DataRow(2,1)]
    [DataRow(3,1)]
    [DataRow(4,2)]
    [DataRow(5,2)]
    [DataRow(6,2)]
    [DataRow(7,3)]
    [DataRow(8,3)]
    [DataRow(9,3)]
    [DataRow(10,4)]
    [DataRow(11,4)]
    [DataRow(12,4)]
    public void MonthReturnsProperQuarter(int month, int expectedQuater)
    {
        var inputDate = new DateTime(2019, month, 1);
        Assert.AreEqual(inputDate.GetQuarterNumber(), expectedQuater);
    }
}

Or, in VB.Net

<TestClass>
Public Class ExtensionTests
    <TestMethod>
    <DataRow(1, 1)>
    <DataRow(2, 1)>
    <DataRow(3, 1)>
    <DataRow(4, 2)>
    <DataRow(5, 2)>
    <DataRow(6, 2)>
    <DataRow(7, 3)>
    <DataRow(8, 3)>
    <DataRow(9, 3)>
    <DataRow(10, 4)>
    <DataRow(11, 4)>
    <DataRow(12, 4)>
    Public Sub MonthReturnsProperQuarter(ByVal month As Integer, ByVal expectedQuarter As Integer)
        Dim inputDate = New DateTime(2019, month, 1)
        Assert.AreEqual(inputDate.GetQuarterNumber(), expectedQuarter)
    End Sub
End Class

So please, don’t continue to use the old way. It’s way past time to move on.