Consider the following scenario. You work for Company A, and they want you to write a simple web portal for them. You learn that there is a process in place already that spits out about 30 images as email attachments, and they would much rather see these displayed on a web site. The images will have to be categorized into main, sub, and chart type. Sounds simple enough, right?

I want to attack this problem from two angles to show how test driven development can, and should, drive your class design. Again, TDD should drive your class design. Simply put: if you do it right, TDD should cause you to actually change how you design your classes and their dependencies into a much looser, flexible, and less brittle pattern.

First, let’s look at it from how one might write this without TDD. This theoretical person is still good with object oriented programming, but hasn’t quite made the leap to TDD. Maybe you fit this bill? Maybe you’ve said “I don’t even know what this is going to look like, so how would I write a unit test for it before it’s written??” to yourself in the past? Let’s get started.

Obviously from this example, one can imagine a Chart object, and perhaps a ChartCategory object:

    public class Chart
    {
        public string FileName { get; set; }
        public ChartCategory Category { get; set; }
    }

    public class ChartCategory
    {
        public string Main { get; set; }
        public string Sub { get; set; }
        public string Type { get; set; }
    }

One might then surmise that they need a way to load charts from disk:

public class ChartReader
    {
        public IEnumerable LoadChartsFromDisk(string path)
        {
            var files = System.IO.Directory.GetFiles(path);

            var charts = files.Select(f => new Chart
            {
                FileName = f
            }).ToList();

            foreach (var chart in charts)
            {
                chart.Category = GetCategory(chart, path);
            }

            return charts;
        }

        private ChartCategory GetCategory(Chart chart, string path)
        {
            var doc = XDocument.Load(path + "\charts.xml");
            var chartElement = doc.Descendants("chart").FirstOrDefault(x => x.Attribute("filename").Value == chart.FileName);
            if (chartElement != null)
            {
                return new ChartCategory
                {
                    Main = chartElement.Attribute("maincategory").Value,
                    Sub = chartElement.Attribute("subcategory").Value,
                    Type = chartElement.Attribute("charttype").Value
                };
            }
            return new ChartCategory();
        }
    }

GREAT! You even had the foresight to load categories from an XML document. This is going to work well, right? Let’s say you wire this all together and make a great front end website for them (I’m going to skip this part since it’s not all that relevant right now). You have your charts displaying on page, and business likes how it looks. You get a pat on the back for getting this done in a few days.

Now they want to add new features. They want charts to have a description. Come to find out, charts are only ever categorized into two main categories, and two static chart types, and they always want one chart type on the left of the page, and the other on the right. You make these changes. Later down the line, someone comes back and says well there could be a different chart type, and this chart type is actually a grouping of charts that, as a group, have to be shown together.

The point I’m trying to make is that this code is brittle and untestable. One guaranteed point in software development is that the requirements will change over time, and the later in the process you get change, the riskier it is.

Why is this code untestable?

Well, technically it’s not impossible to test this code, but let’s try writing a simple unit test. One that proves a chart gets its category from the xml document:

        [TestMethod]
        public void charts_get_their_categories_from_xml_documents()
        {
            // Arrange
            var reader = new ChartReader();

            // Act
            var charts = reader.LoadChartsFromDisk("c:\unittests").ToList();

            // Assert
            Assert.AreEqual("main", charts.First().Category.Main);
            Assert.AreEqual("sub", charts.First().Category.Main);
            Assert.AreEqual("type", charts.First().Category.Main);
        }

Halfway through this test, it becomes apparent that the ChartReader object has 2 major dependencies: the file system, and the xml document. That is to say, in order to pass this test, the C:unittests folder will have to contain image files and an xml document to simulate the assertions. This causes a few problems:

  1. Disk IO is slow
  2. This test is very brittle, as the arrange section is not really arranging anything.
    1. The test parameters are setup on disk and are easy to modify by anyone
  3. In order to deploy this to any type of build server that runs unit tests, the same filesystem dependencies need to be met.

While this class design got the job done, I hope you can see that it is not ideal, but how can TDD help? Click here to find out!

One thought on “Test driven development (TDD) should drive your class design (Part 1 of 2): The wrong way

  1. Pingback: Test driven development (TDD) should drive your class design (Part 2 of 2): The right way | Geek Powers

Leave a Reply

Your email address will not be published. Required fields are marked *