Data driven unit testing (Part 2)

In part 1 (way back in August 2005), we developed a basic unit test that we are now going to data drive to perform all the tests we defined using a cyclomatic complexity analysis of the code.

What is a data driven test?

A data driven test is essentially a normal test that has been wrapped in a loop, and the test is run several times with a different set of values each time. The values can be supplied from any source, such as a csv, excel file or a database. In this example I am going to use the new testing context and a 2005 SQL Express database. Using an MDF file allows it to be added to the solution along with all the other source files and easily version controlled.

To add a database, with the test project selected, Click on the the Project menu, then Add New Item. When the add new item dialogue appears, select SQL database. I am going to call my database TriangleTests.mdf. Ok, assuming the database creation has worked for you, (there was a bug in beta 2), we will now continue on our merry way and create the TestData table in our new database with the following SQL.

CREATE TABLE [dbo].[TestCases] {
    [TestNumber] [int] IDENTITY(1,1) NOT NULL,
    [FirstValue] [int] NOT NULL,
    [SecondValue] [int] NOT NULL,
    [ThirdValue] [int] NOT NULL,
    [Result] [nvarchar] (50) NOT NULL,
    [IsNegativeTest] [bit] NOT NULL,
    [TestDescription] [nvarchar] (50) NOT NULL
} on [PRIMARY]
Modifying our existing test

Once our database has been created, we will modify the existing test shell to add a TestProperty attribute that allows us to access the data in the table using the new TestContext. We will also modify the test itself to read the values from the database instead of hard coding them.

The easiest way to add the DataSource attribute is to select CheckTriangleTest in the TestView window then click on the ellipsis in the empty Data Connection String field in the property window. Then, select the TriangleTests.MDF file and the TestCases table using the drop down in the Data Table Name field. We also need to remove the hard coded values and use trhe TestContext.DataRow Our test now looks as follows:

[TestMethod()]
[DataSource("System.Data.SqlClient",
    "Data Source=.\SQLEXPRESS;AttachDbFilename=\"Your Connection String",
    "TestCases", DataAccessMethod.Sequential)]

public void CheckTriangleTest() { int a = (int)TestContext.DataRow[1]; int b = (int)TestContext.DataRow[2]; int c = (int)TestContext.DataRow[3]; string expected = (string)TestContext.DataRow[4]; string actual = Teknologika.Samples.Samples.CheckTriangle(a, b, c); Assert.AreEqual(expected, actual, "CheckTriangle did not return the expected value."); } Implementing the CheckTriangle method

To implement our CheckTriangle method, I am just going to cut and paste the code that we wrote previously, as the CheckTriangle implementation in Samples.cs.

public static string CheckTriangle (int a, int b, int c)
{
    if (a <= 0 || b <= 0 || c <= 0)
    {
       return "Illegal Triangle";
    }

if ((a &gt; b + c || b &gt; a + c || c &gt; a + b))
{
   return "Illegal Triangle";
}

if (a == b &amp;&amp; b == c)
{
   return "Equilateral Triangle";
}

if (a == b || b == c || a == c)
{
   return "Isosceles Triangle";
}

return "Scalene Triangle";

} With the method now implemented, all we need to do is to add the tests we defined to the database and then run our tests. This approach significantly reduces the lines of code needed to execute all the paths through our code and allows new tests to be added quickly, and easily.