Glenn’s Wedding App – Using Test Driven Design

6 08 2008

Here at DWS, I’m “On the Bench”. This essentially means I don’t have any client work to do so I’ve been doing things for DWS internally as well as professional development. With this spare time, I’ve been practicing my skills in XML, .NET 3.5, and TDD (Test Driven Design).

I’m not going to pretend that I know everything about TDD, I don’t. What I will do, however, is tell you about my understanding of this fast way of designing good quality software that has fantastic code coverage as well!

I used to code in .NET 2.0 a lot at home and in TAFE (a technical college where I learned software development): as well as .NET 3.0 as part of my job here at DWS. Having said that, much of my work for the last 9 months or so has been testing, specifically leading UAT at a client’s site, so my coding skills are a little rusty.

I wanted to sharpen these coding skills but first I needed a project. Too easy! My upcoming wedding to my darling fiancee Jenna is a great inspiration for a project! So, I started a project that tracks wedding-related events: you know those events, engagement party, pre-wedding parties, organising the wedding, etc. My project will tell me which events are coming up next as well as how much time there is until the event.

Now, where does Test Driven Design come in to this? Well, it’s a great way of getting software written quickly with high quality, so that’s the method I have chosen to write my program. So, what is Test Driven Design?

Test Driven Design as I understand it is a process where you begin with a single concept. This concept says that any piece of software may either be complete or incomplete. Any defects are just where the software is incomplete. To demonstrate this, a test is written that confirms the software is incomplete and provides a proof for when it is complete. It starts with writing a test case that describes a little of the functionality of the final product.

Once the test case is written, the test case is compiled. At this stage, there is no production code and so the compilation of the test case fails. Let’s take a look at an example:

[TestMethod]
public void DoStuffTest()
{
  Something target = new Something();
  bool expected = true;

  target.DoStuff();
  bool actual = target.StuffDone;

  Assert.AreEqual(expected, actual);
}
Example 1: Initial Test Case

So, there’s a test. The idea here is that Something doesn’t exist, so the compiler fails to compile the test. Let’s add Someting.

public class Something { }
Example 2: Inital Production Code

Ok, so I know what you’re thinking, “He’s left out all the boring stuff.” Not at all, that’s it! There isn’t anything more. Let’s compile… Compilation Failed! Error: DoStuff() is not defined.

That’s right, we have stubbed the class but haven’t filled in the methods yet. Simply put, we didn’t do that because it wasn’t necessary to fix the problem at hand. That’s what TDD is about, fixing what is at hand to the very minimum required to satisfy the tests. As a result, you get very high code coverage, as in greater than 90%, often 100%, and the code is simple and testable.

We continue down this path until we finally get a compiled program: Compilation Successful! Now we run the test…

Test Failed: Expected: true, Actual: false

What happened? Let’s look at the code.

public class Something
{
  public void DoSomething()
  { }

  public bool StuffDone
  {
    get { return false; }
  }
}
Example 3: Production Code - Meeting the minimum requirements of the test case

Ah, this is easy to fix:

...
get {return true;}
...

Now it passes! Fantastic! But now we have only proven that we can know when something has happened, what about before it’s happened? Let’s change our test:

[TestMethod]
public void DoStuffTest()
{
  Something target = new Something();
  bool expected1 = false;
  bool expected2 = true;

  bool actual = target.StuffDone;
  Assert.AreEqual(expected1, actual);

  target.DoStuff();
  actual = target.StuffDone;
  Assert.AreEqual(expected2, actual);
}

Example 4: Updating the Test Case

Now, see the change? We check target.StuffDone before we call DoStuff() and expect it to return false. Of course it should be false, we haven’t “done stuff” yet! Let’s compile: Compilation Successful. Not a problem, it only exercises existing production code. Let’s run the test:

Test Failed: Expected: false, Actual: true.

Ok, so what happened? Remember how we made our code work by statically returning true from the StuffDone property? We’re going to have to be a little smarter now.

How can we get the test to pass with the simplest change to the production code? Really, the best way is to change the result of StuffDone when we call DoStuff(). Because the first Assert statement expects StuffDone to be false, that’s what we’ll initialize it to satisfy that test.

// Something that does stuff.
public class Something
{
  private bool mStuffDone = false;

  // Do something.
  public void DoSomething()
  { mStuffDone = true; }

  // Has something been done? Return true if yes.
  public bool StuffDone
  {
    get { return mStuffDone; }
  }
}
Example 5: The Completed Production Code

Easy! Now, I know what you’re thinking, it’s easy because it’s just some simple example. Well, yes, that’s right but it’s also how TDD works. Everything is broken down in to simple bite-sized pieces. Now, think about what we have here. We have a fully tested piece of production code, a simple design, and a test that proves that we have delivered on the requirement described by the test case: let’s just say, green ticks are hard to argue with.

You’ll likely want to refactor your code at some stage but at least you won’t be refactoring too early and you have test cases to check that your refactoring doesn’t break anything. Try writing test cases around the requirements in your projects. I find it easy and rewarding.

Kind Regards
Glenn