10 reasons to use TDD (Test Driven Development)

  1. It will help you improve your OOD

    Proper object oriented design is the key to writing good extendable, maintainable, and stable software. If you pile too much functionality into one big class or one big method, you’re just asking for trouble. TDD makes it easier to adhere to the SRP (Single Responsibility Principle) by encouraging you to create smaller classes with less functionality.

  2. Get it in front of your users faster

    Designing your classes to inherently rely on abstract (mockable) dependencies is a sure way to get on the fast track to building a demo. For example, in a database driven application, a mock data layer can be substituted with generated data to be consumed by the front end. Though somewhat unorthodox, mocking frameworks can work just as well to fake a back end system in a running application as they can for unit test mocking! In software development, getting the product in front of your users can be the most important step, because it gives them a chance to change requirements early when it’s less painful.

  3. Good coverage

    TDD will have you writing a test for every bit of functionality you are coding. You are automatically forcing yourself to have a high code coverage metric if you stick to the cadence: Write breaking test, fix test, refactor.

  4. Quickly verify all functionality

    When refactoring code, it is very useful to be able to quickly verify all existing functionality in an instant. This isn’t necessarily a benefit of TDD itself, but rather having good unit test coverage of business rules. Sometimes in an emergency situation (e.g. an outage due to code bug), we are forced to cut corners and not necessarily write a test first. Having that safety net of unit tests there is a huge confidence booster. Naturally by developing with TDD, you are building a safety net as you go!

  5. It forces you to rely only on abstractions (Dependency Inversion Principle).

    You wouldn’t solder a lamp directly to the electrical wiring in a wall, would you?

    The dependency inversion principle is one of the 5 SOLID principles of object oriented design. It states that when designing a class, any and all of the other classes that are used should be via abstractions. That is to say, a class should not reference another concrete type. When done correctly, test driven development encourages adherence to this principle, because you will always need something to mock when writing a test.

  6. Smaller problems are easier to solve.

    (2 + 2) / 2 – 2 * 6 = ?

    If you understand the order of operations (if you’re here, I’m sure you do), then your brain automatically broke this equation down into solvable parts. Likely, you figured out that 2+2 is 4, and 2 * 6 is 12, so the equation became 4/2 – 12. Then you might just solve 4/2 and finish out with -10. The point is that you broke the larger problem down into smaller chunks, because that’s the easiest way to get to the answer. (Bonus points if you can just look at equations like that and spit out an answer!). Any programmer worth their salt isn’t going to attack a large application by writing one big blob of code. They’re going to understand what the customer wants, break it down into pieces, and build those pieces to fit together for the larger system. TDD is a great way to do just that without completely understanding the big picture immediately.

  7. It feels really good

    I’ve done quite a few projects with TDD now. The first time it feels strange, like it can’t possibly work. You toil for a few days or weeks on writing these individual little bits, solving little tiny problems as you go. The larger problem is not necessarily in your brain the entire time, so it feels foreign. Finally, when it comes time to make a demo, you get to connect all the little pieces, which almost always involves an IoC container for me. This is a very satisfying process and brings me a lot of joy.

  8. Opens up the path for future testing

    This is a topic I have talked about at length. Some may not see the value in this immediately, but I find this extremely important. Simply by following the TDD pattern, you are ensuring future testability of your classes. No one writes bug-free code every time. I can’t tell you how many times I have been seriously happy when it comes time to fix a bug in code that I’ve used TDD with. I come back to find that in order to reproduce the bug, I just have to provide a very specific mock up in a new unit test. The path was laid by me in the past, and now it is super easy to prove that the bug is fixed by fixing a failed unit test.

  9. Stops analysis paralysis

    From Wikipedia:

    Analysis paralysis or paralysis of analysis is an anti-pattern, the state of over-analyzing (or over-thinking) a situation so that a decision or action is never taken, in effect paralyzing the outcome.

    Sure, any new application needs some analysis, but when the above happens, nothing gets done. TDD allows you to get started right away by solving small problems immediately. Sometimes, the bigger picture starts to come together when you start chipping away at the little bits.

  10. Slow is smooth, and smooth is fast

    This is an old saying applied to targeting a firearm. The saying explains that if you move too fast, you’re going to make a mistake and fail. I believe the same saying can be applied to software development as well. The argument against TDD and unit tests in general that I’ve heard in the past is that they slow you down. It’s a natural thought to have: I can either start writing code to solve the problem, or start writing code to test non-existent code, and then write the same code to solve the problem anyway.

    WRONG!

    This argument infuriates me, because it typically comes from someone of power who is trying to justify cutting corners. Sure, if you put two people on solving the same complex problem, one with TDD and one hacking directly towards a solution, the latter is going to finish quicker, but that’s just not a real life scenario long term. With any given application, someone is going to need changes. While TDD might take longer in the initial phases, it pays dividends extremely quickly when changes start pouring in. The class design is decoupled, class responsibilities are seriously limited, so requirements changes very rarely actually mean changing working code! Instead, it is safer and quicker to extend and write new code. Less bugs are created, and new features can be added a lot quicker in the long run.

Test driven development (TDD) should drive your class design (Part 2 of 2): The right way

Update (9/16/2013): Example source code is now attached, including source for parts 1 and 2.

In the previous post, I proposed a simple example:

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

In that post, I outlined how one might develop a library for such a site without TDD. Erase that code from your memory. DELETE.

In this post, I will be showing how test driven development can vastly improve the flexibility of the code you write, using the same example. I will be using a 3rd party tool called JustMock from Telerik. I highly recommend at least finding some mocking tool, as this will greatly enhance the TDD process. You can read a previous post for more on the subject of dependency injection, IoC, and mocking. It is through these principles and patterns that TDD becomes a really powerful method for class design. Let’s get started!

Sometimes staring at a blank test class can be rather daunting.

dont-panic-circle

Calm down!

DON’T PANIC!

LET GO!

*SLAP*

Feel better? Me too… I got to slap you!

In all seriousness, think of the test area as your white board. Your API isn’t written yet, but you get to use it already!

What is the most basic thing you need to do? Load charts from somewhere right? What holds things? A container? Perhaps… but, let’s call it a Repository. We know it needs to return an enumerable collection of Chart objects. We also know that something is going to depend on this “Repository” object to load its charts from. Let’s say we haven’t settled on the architecture yet (MVC, WebForms, HTML5 with a WebAPI service). We need to test that SOMETHING can load the charts using a repository just to get started, so let’s call it ChartLoader. I think we have enough to write our first unit test. I typically start by renaming the class and first test method, and inserting my standard AAA comments:

	[TestClass]
    public class when_loading_charts
    {
        [TestMethod]
        public void chart_loader_returns_charts_from_repository()
        {
            // Arrange
            // Act
            // Assert
        }
    }

TDD has a cadence to it. You will:

  1. Write a test.
  2. The you run all tests.
    1. New test fails because the requirement isn’t met yet
  3. Do the simplest thing to make all tests pass again
  4. Refactor
    1. Only after everything passes do you refactor. It may be tempting, but don’t do it earlier! Get a good baseline first.
  5. Repeat for the next requirement

The first test

	[TestMethod]
	public void chart_loader_returns_charts_from_repository()
	{
		// Arrange
		var container = new MockingContainer<ChartLoader>();
		container.Arrange<IChartRepository>(r => r.Charts).Returns(new List<Chart>
		{
			new Chart
			{
				FileName = "file1.png",
				Category = new ChartCategory
				{
					Main = "main",
					Sub = "sub",
					Type = "type"
				}
			}
		});

		// Act
		ChartLoader loader = container.Instance;
		IEnumerable<Chart> result = loader.GetCharts();

		// Assert
		Assert.AreEqual(1, result.Count());
		Assert.AreEqual("file1.png", result.First().FileName);
		Assert.AreEqual("main", result.First().Category.Main);
		Assert.AreEqual("sub", result.First().Category.Sub);
		Assert.AreEqual("type", result.First().Category.Type);
	}

So, we have a test for this fictitious ChartLoader object, and we know it needs to load from a repository, so ChartLoader needs some abstract definition of a repository (IChartRepository), and we know it needs to get charts… IChartRepository.Charts is going to be an IEnumerable, but it’s undefined so far. The Arrange call is essentially setting up a return value from the chart repository, so that when ChartLoader hits it, it will get the list as defined in the .Returns() call. We’ll see that in the next code section. So, the next thing to do is call the method in the ChartLoader to get the charts, and then assert that everything the ChartLoader returned, matched exactly what the repository returned (and you know exactly what it’s going to return, because you mocked it!)

So now there is a test with a bunch of red lines, because nothing is real yet. This is why I love tools like ReSharper, which let me auto generate the classes very quickly. What gets generated:

    public interface IChartRepository
    {
        IEnumerable<Chart> Charts { get; }
    }

    public class ChartLoader
    {
        public IEnumerable<Chart> GetCharts()
        {
            throw new NotImplementedException();
        }
    }

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

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

Now, the test will fail, but at least everything compiles. This is where you go to step 4 in the process. Do the simplest thing to make all the tests pass. In this case it’s 1 test, but get in the habit of running all tests… find the keyboard shortcut for doing this and learn it! Optionally, if you’re really into it, you can get a tool that automatically runs your tests like NCrunch

Here, there is a certain pattern that IoC containers work with: Your class constructor should take parameters for all of its dependencies. Here is the implementation of ChartLoader I came up with to get the tests to pass:

    public class ChartLoader
    {
        private readonly IChartRepository _chartRepository;

        public ChartLoader(IChartRepository chartRepository)
        {
            _chartRepository = chartRepository;
        }

        public IEnumerable<Chart> GetCharts()
        {
            return _chartRepository.Charts;
        }
    }

Now typically, you will do whatever refactoring that is necessary, which includes moving classes into appropriate folders and projects, etc, because all of the generated code I’ve shown is at the bottom of the test file. That would be out of the scope of this post, though, and sometimes it’s ok to leave them inline while you’re still writing tests.

This might actually be a good time to create a fake IChartRepository implementation on the front end site and actually give the business something to look at!

Hah! Did you think TDD was slow? How much quicker did we just deliver something to business and get feedback? This is VERY early on, and it is invaluable, because business requirements change when the people using a system get to play with it. You can and should show them something as soon as possible.

Back to TDD. The second test:

We have an object that can get charts, but we still don’t have anything loading from disk. Let’s write another test (because in TDD we always write a test before a feature):
I typically start a new test class when I’m dealing with a new system under test, but for this example, I won’t bother.

        [TestMethod]
        public void chart_repository_returns_charts_from_directory()
        {
            // Arrange
            var container = new MockingContainer<DirectoryChartRepository>();
            container.Arrange<IDirectory>(d => d.GetFiles()).Returns(new List<string>
            {
                "file1.png",
                "file2.png"
            });

            // Act
            DirectoryChartRepository repository = container.Instance;
            IEnumerable<Chart> result = repository.Charts;

            // Assert
            Assert.AreEqual(2, result.Count());
            Assert.AreEqual("file1.png", result.ElementAt(0).FileName);
            Assert.AreEqual("file2.png", result.ElementAt(1).FileName);
        }

Here I just want to prove that charts can load from a directory. Now, what really makes this a special scenario (and why I chose this example) is that there is an inherent dependency on the file system in the requirement: Charts will be loaded off the hard drive. This is about class design and controlling inputs in order to test things properly, though. I don’t want a hard drive to have to spin up in order to run a unit test. Tests should execute in milliseconds and be self contained units. The arrange section of a unit test HAS TO contain all of the dependency mocking, or it is going to be a brittle test that requires too much setup. Furthermore, System.IO.Directory.GetFiles(path) is static and can’t be overridden, so it’s impossible to mock (without a major effort or something that can fiddle with system libraries at runtime).

So, now let’s do some more class generation.

    public interface IDirectory
    {
        IEnumerable<string> GetFiles();
    }

    public class DirectoryChartRepository : IChartRepository
    {
        public IEnumerable<Chart> Charts { get; }
    }

So now everything compiles, but the new test still fails, because the DirectoryChartRepository object doesn’t actually use IDirectory and isn’t building any chart objects from the directory. So we have to do more to get it to pass:

    public class DirectoryChartRepository : IChartRepository
    {
        private readonly IDirectory _directory;

        public DirectoryChartRepository(IDirectory directory)
        {
            _directory = directory;
        }

        public IEnumerable<Chart> Charts
        {
            get
            {
                // TODO: I'm going to need a way to inject the path
                return _directory.GetFiles().Select(f => new Chart
                {
                    FileName = f
                });
            }
        }
    }

Now both tests should pass, and we can move on. How do charts get categories? NEW TEST! Seeing the pattern now? (Good, because I’m going to go faster):

Going faster. The third test:

	[TestMethod]
	public void chart_repository_categorizes_charts_from_categorizer()
	{
		// Arrange
		var container = new MockingContainer<DirectoryChartRepository>();
		container.Arrange<IDirectory>(d => d.GetFiles()).Returns(new List<string>
		{
			"file1.png"
		});

		container.Arrange<IChartCategorizer>(c => c.GetChartCategory(Arg.IsAny<Chart>()))
			.Returns(new ChartCategory
		{
			Main = "main",
			Sub = "sub",
			Type = "type"
		});

		// Act
		DirectoryChartRepository repository = container.Instance;
		IEnumerable<Chart> result = repository.Charts;

		// Assert
		Assert.AreEqual("main", result.ElementAt(0).Category.Main);
		Assert.AreEqual("sub", result.ElementAt(0).Category.Sub);
		Assert.AreEqual("type", result.ElementAt(0).Category.Type);
	}

    public interface IChartCategorizer
    {
        ChartCategory GetChartCategory(Chart chart);
    }

Tests compile but fail again. Add a dependency on IChartCategorizer to the DirectoryChartRepository, and use it. New DirectoryChartRepository class:

    public class DirectoryChartRepository : IChartRepository
    {
        private readonly IDirectory _directory;
        private readonly IChartCategorizer _chartCategorizer;

        public DirectoryChartRepository(IDirectory directory,
            IChartCategorizer chartCategorizer)
        {
            _directory = directory;
            _chartCategorizer = chartCategorizer;
        }

        public IEnumerable<Chart> Charts
        {
            get
            {
                return _directory.GetFiles().Select(f =>
                {
                    var chart = new Chart
                    {
                        FileName = f
                    };
                    chart.Category = _chartCategorizer.GetChartCategory(chart);
                    return chart;
                });
            }
        }
    }

Tests pass. Things are really starting to come together at this point. We still want to load categories from that XML file perhaps, but who’s going to maintain the XML file? What if they want to be able to manage categories themselves? Things are really starting to get going now, and with this class design, to implement any categorization requirements, it’s simply an implementation of IChartCategorizer. XMLChartCategorizer or SQLChartCategorizer? Well for now let’s do DictionaryChartCategorizer since we’re going to be the only ones maintaining the categories for now.

Fourth test:

	[TestMethod]
	public void dictionary_categorizer_loads_categories_from_dictionary()
	{
		// Arrange
		var container = new MockingContainer<DictionaryChartCategorizer>();
		container.Arrange<IChartCategoryDictionaryProvider>(d => d.GetCategories()).Returns(new Dictionary<string, ChartCategory>
		{
			{
				"file1.png",
				new ChartCategory
				{
					Main = "main",
					Sub = "sub",
					Type = "type"
				}
			}
		});

		// Act
		var result = container.Instance.GetChartCategory(new Chart
		{
			FileName = "file1.png"
		});

		// Assert
		Assert.AreEqual("main", result.Main);
		Assert.AreEqual("sub", result.Sub);
		Assert.AreEqual("type", result.Type);
	}

    public interface IChartCategoryDictionaryProvider
    {
        Dictionary<string, ChartCategory> GetCategories();
    }

    public class DictionaryChartCategorizer : IChartCategorizer
    {
        public ChartCategory GetChartCategory(Chart chart)
        {
            throw new NotImplementedException();
        }
    }

Partway through writing the Arrange section, I realize that I need a way to control the dictionary of categories, and this is a good time to make a new IChartCategoryDictionaryProvider object, because I can implement that interface for XML, SQL, a Code Only version, or this mock version! We have a compiling test that fails due to exceptions. Let’s fix that:

    public class DictionaryChartCategorizer : IChartCategorizer
    {
        private readonly IChartCategoryDictionaryProvider _chartCategoryDictionaryProvider;

        public DictionaryChartCategorizer(IChartCategoryDictionaryProvider chartCategoryDictionaryProvider)
        {
            _chartCategoryDictionaryProvider = chartCategoryDictionaryProvider;
        }

        public ChartCategory GetChartCategory(Chart chart)
        {
            var categories = _chartCategoryDictionaryProvider.GetCategories();
            ChartCategory category;
            if (categories.TryGetValue(chart.FileName, out category))
            {
                return category;
            }
            return null;
        }
    }

With that new implementation of DictionaryChartCategorizer, everything passes again. So, we have the basic structure of our API down that satisfies all of the requirements, but STILL no call to System.IO.Directory.GetFiles() and no loading charts from XML? What gives? We’ve reached the point where we need real implementations in order to call this done, but we could easily skip that and write the whole front end website. As noted, we could have done that a long time ago as soon as we had the ChartLoader object.

Now let’s finish this thing out

The test for this is basic, because there’s not much to arrange or assert:

	[TestMethod]
	public void filesystem_directory_does_not_throw_exception()
	{
		// Arrange
		var directory = new FileSystemDirectory("C:\");

		// Act
		var result = directory.GetFiles();

		// Assert
		// Pass if no exceptions
	}

    public class FileSystemDirectory : IDirectory
    {
        private readonly string _path;

        public FileSystemDirectory(string path)
        {
            _path = path;
        }

        public IEnumerable<string> GetFiles()
        {
            return System.IO.Directory.GetFiles(_path);
        }
    }

You’ll notice I didn’t mock anything, because this class doesn’t have a mockable dependency. It goes directly to a static method to get files off the disk, and that’s exactly what we didn’t want polluting our classes making testing them hard. It has to be done at some point, though, and at least we have a single class to handle it and it’s tucked away neatly in an abstraction. As a side note, this opens up the possibility of having a non filesystem directory (store image byte arrays in SQL?), which is a pretty neat concept, and no code would have to change.

Almost done!

	[TestMethod]
	public void xml_categorizer_loads_categories_from_xdoc()
	{
		// Arrange
		var document =
			new XDocument(new XElement("charts",
				new XElement("chart", new XAttribute("maincategory", "main"), new XAttribute("subcategory", "sub"),
					new XAttribute("type", "type"), new XAttribute("filename", "file1.png"))));

		var categorizer = new XmlChartCategorizer(document);

		// Act
		var category = categorizer.GetChartCategory(new Chart
		{
			FileName = "file1.png"
		});

		// Assert
		Assert.AreEqual("main", category.Main);
		Assert.AreEqual("sub", category.Sub);
		Assert.AreEqual("type", category.Type);
	}

    public class XmlChartCategorizer : IChartCategorizer
    {
        private readonly XDocument _document;

        public XmlChartCategorizer(XDocument document)
        {
            _document = document;
        }

        public ChartCategory GetChartCategory(Chart chart)
        {
            var chartElement = _document.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("type").Value
                };
            }
            return null;
        }
    }

That’s it. We have everything we need, and the library has tests for all business functionality. The design is a LOT different, no? You might not see the value in such a design pattern at first, but consider how flexible it is to change. I’ve already outlined some changes that would be very simple, but more than just simple changes, even complex changes are not going to damage the integrity of the rest of the library. Changing the Categorizer implementation to a SQLChartCategorizer does not have anything to do with where the files come from on disk. Files could come from cloud blob storage, and categories could come from an infinite monkey array… the rest of the code won’t care.

If you implement the infinite monkey array, though, I want to see the code.


(Contains 1 attachments.)