Shapejam

Trying to follow Buckminster Fuller's example in life, one step at a time to bring you brain scrapings for a new millennium.

How I Discovered A Love For Test Driven Development... Part 2

27 May 2013 - Nic Ho Chee

In a previous post we covered an introduction to Test Driven Development. In brief it is a process by which a programmer can create simple, automated tests which capture application logic and allow for sections of the code to be continually validated. You can have a read at the link above...

This post aims to give a boot-strap introduction to NUnit and shows how some simple principles could be used to ensure decent coverage for a chosen problem.

The Problem

The power industry uses Electricity Forward Agreements (EFA) to trade electricity amongst the various companies that produce, consume or speculate in energy in the UK. Units traded tend to be some fraction or multiple of 1 Megawatt Hour (MWh) and physical trades will have a delivery component which will need to take account of the EFA calendar to calculate the total amounts destined for delivery.

The EFA calendar in brief breaks down as follows:

  1. An EFA delivery day is 24 hours long, starting at 11pm and ending at 11pm the following day.
  2. A day is split into six blocks numbered 1 through to 6, each of which is four hours long.
  3. An EFA week contains seven days as normal and starts at 11pm on a Sunday.
  4. An EFA month can be either four or five weeks depending on when it falls in the year. The first three months are split into 4/4/5 weeks so January and February are four weeks long while March is five weeks long. Some of the days in the standard calendar month of January for example may fall in the EFA February month.
  5. An EFA quarter is 13 weeks long, containing two four week EFA months and one five week EFA month.
  6. An EFA year starts at 11pm on the Sunday of the week containing the first Thursday of the year and contains weeks numbered 1 to 52.

Some method we have yet to create will generate the volume of energy over some period of time. The granularity can be less than a block and may be in the range one to fifteen minutes in some cases. How do we ensure that each delivery minute is attributed to the correct EFA week so that we can calculate the total energy volume per EFA week for a single year?

Breaking The Problem Down

Before we start thinking about possible solutions to the question of calculating energy delivery volumes we should think about how we can test any results obtained. As a first step we know that for a given unit of energy, we will discover in which EFA week the energy is to be delivered. For this we'll need to find which EFA week contains any given instantaneous timestamp which will probably be stored in a C# DateTime structure. We have just uncovered something we can create a unit test for!

If the flow of energy is measured each minute there will be at least 525600 data points generated each year and to create a useful test for the complete set of data will be impossibly time-consuming and error-prone for your average (or even better than average) programmer. Exhaustive testing of most systems is impossible given problem complexity so some simple choices should be made to try and give the most coverage for the smallest coding cost.

Since we're interested in working out which week a given minute might fall in and we know there are only 52 weeks in a year, a lot of the data is effectively equivalent. We want to partition the data points to test into sets containing essentially the same elements. This is a technique known as Equivalence Partitioning. Given the information we know so far we only need to separate the data into 52 equivalent buckets, one for each week in the year.

We have reduced 500k+ data points into 52 equivalent sets, so we now need to work out which of these we're going to test. A decent heuristic is that the most likely source of error will be at the intersection between the sets, so we want to test a data point either side of that position. This is a selection process known as Boundary Value Analysis. which would yield a total of 104 data points to test :). The problem states that we're only working with a single year, so we shouldn't include any un-necessary information, which in this case would include the last point from the previous year and the first point from the following year. Changes across EFA year boundaries could be rolled into another test once the specification changes.

In the past I have added an extra random datapoint for each equivalent set to satisfy my curiosity. There are now 156 data points for a programmer to validate through some other mechanism and add into an automated test.

Hello NUnit My Old Friend...

To try out this example you need the following tools:

The recipe:

// Simple testsuite which will check for weekly granularity 

using System;
using NUnit.Framework;

namespace EFATests
{
    /// <summary>
    /// A publically accessible class containing a set of tests and optional SetUp and TearDown methods.
    /// </summary>
    [TestFixture]
    public class WeekTestFixture
    {
        #region Setup

        /// <summary>
        /// Called before each test in the class is run to put the system in some initial state.
        /// </summary>
        [SetUp]
        public void FixUp()
        {
            // We may need to do something in here.
        }

        /// <summary>
        /// Called after each test in the class is run to perform some resource cleanup etc.
        /// </summary>
        [TearDown]
        public void CleanUp()
        {
            // We may need to do something in here.
        }

        #endregion

        #region Test Cases

        /// <summary>
        /// Check that for a range of data the given minutes are attributed to the correct EFA week.
        /// </summary>
        [Test]
        public void DateTimesIn2013HaveTheCorrectEfaWeek()
        {
            Assert.That(false);
        }
        
        #endregion
    }
}

using System;
using NUnit.Framework;
using System.Collections.Generic;

namespace EFATests
{
    /// <summary>
    /// A publically accessible class containing a set of tests and optional SetUp and TearDown methods.
    /// </summary>
    [TestFixture]
    public class WeekTestFixture
    {
        #region Member Variables

        /// <summary>A list containing the various weekly data points in the year 2013.</summary>
        private IList<Tuple<DateTime, int>> m_efaWeekDataPoints2013;

        #endregion

        #region Setup

        /// <summary>
        /// Called before each test in the class is run to put the system in some initial state.
        /// </summary>
        [SetUp]
        public void FixUp()
        {
            m_efaWeekDataPoints2013 = new List<Tuple<DateTime, int>>();

            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2012, 12, 30, 23, 00, 00), 1));
            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 1, 6, 22, 59, 59), 1));
            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 1, 6, 23, 00, 00), 2));
            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 1, 13, 22, 59, 59), 2));

            // Add the rest of the dates in 2013 here.

            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 12, 15, 23, 00, 00), 51));
            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 12, 22, 22, 59, 59), 51));
            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 12, 22, 23, 00, 00), 52));
            m_efaWeekDataPoints2013.Add(new Tuple<DateTime, int>(new DateTime(2013, 12, 29, 22, 59, 59), 52));

            // Add some random dates in 2013 here.
        }

        /// <summary>
        /// Called after each test in the class is run to perform some resource cleanup etc.
        /// </summary>
        [TearDown]
        public void CleanUp()
        {
            m_efaWeekDataPoints2013 = null; 
        }

        #endregion

        #region Test Cases

        /// <summary>
        /// Check that for a range of data the given minutes are attributed to the correct EFA week.
        /// </summary>
        [Test]
        public void DateTimesIn2013HaveTheCorrectEfaWeek()
        {
            foreach (Tuple<DateTime, int> testValue in m_efaWeekDataPoints2013)
            {
                EfaDateTime dateTime = new EfaDateTime(testValue.Item1);
                Assert.That(dateTime.GetEfaWeek() == testValue.Item2, 
                    "DateTime " + testValue.Item1.ToString() + " was placed in EFA Week " + dateTime.GetEfaWeek() + ", we expected " + testValue.Item2);
            }
        }
        
        #endregion
    }
}

The programmer is now in a good place to be able to fill out the stub method and return an EFA week from a DateTime in the 2013 EFA year.

Next...

Dependency injection and what you can do with it...