A walkthrough of the story-test-driven-development of the code at http://svn.agilemaryland.org/AddressbookWin/


r6 | gdinwiddie | 2008-02-01 10:46:42 -0500 (Fri, 01 Feb 2008) | 4 lines

Add the first, rudimentary test. Not that this doesn't compile, as the class has not yet been created. Normally, I wouldn't checkin at this point, but I want to capture the small-scale steps in this project.

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

namespace Addressbook
{
    [TestFixture]
    class AddressbookTests
    {
        [Test]
        public void EnsureConstructor()
        {
            Addressbook it = new Addressbook();
        }
    }
}


r7 | gdinwiddie | 2008-02-01 11:02:10 -0500 (Fri, 01 Feb 2008) | 4 lines

Now I've created the class-under-test. Note that I had to correct a couple of things in the test case, also. The test doesn't test anything yet, but it gives us a green bar.

using System;
using System.Collections.Generic;
using System.Text;

namespace AddressbookWin
{
    public class Addressbook
    {
    }
}


r8 | gdinwiddie | 2008-02-01 11:05:16 -0500 (Fri, 01 Feb 2008) | 3 lines

Now I've added a simple assert to the test. Still not much value, though. It's time to think about what story we want to accomplish.

        [Test]
        public void EnsureConstructor()
        {
            Addressbook it = new Addressbook();
            Assert.That(it, Is.Not.Null);
        }


r9 | gdinwiddie | 2008-02-01 11:18:47 -0500 (Fri, 01 Feb 2008) | 5 lines

OK, we've added a small User Story. It fails, of course, because we've done nothing (except creat and Address class) to make it pass. I've given it a Category of "StoryTest" so that we can temporarily ignore it using the Categories tab in Nunit.

namespace AddressbookWin
{
    [TestFixture]
    public class AddressbookTests
    {
        Addressbook it;

        [Test]
        [Category("StoryTest")]
        public void AUserCanAddAndRetrieveAnAddress()
        {
            Address toInsert = new Address();
            it.Add(toInsert);
            List<Address> retrieved = it.AllAddresses();
            Assert.That(retrieved, Has.Member(toInsert));
        }

        [Test]
        public void EnsureConstructor()
        {
            Assert.That(it, Is.Not.Null);
        }

        [SetUp]
        public void SetupForAddressbookTests()
        {
            it = new Addressbook();
        }
    }
}

Note that I've also refactored duplicated code from the two tests into a SetUp method that runs before each Test.


r10 | gdinwiddie | 2008-02-01 11:24:15 -0500 (Fri, 01 Feb 2008) | 6 lines

Let's start with a thin slice of that story, that before we add any addresses, the addressbook is empty.

Again, I wouldn't normally check this in with the test failing, but for this project I want to record the partial steps.

        [Test]
        public void EnsureANewAddressbookIsEmpty()
        {
            List<Address> retrieved = it.AllAddresses();
            Assert.That(retrieved, Is.Empty);
        }

And just enough to make it compile

namespace AddressbookWin
{
    public class Addressbook
    {
        public void Add(Address toInsert)
        {
            throw new NotImplementedException();
        }

        public List<Address> AllAddresses()
        {
            throw new NotImplementedException();
        }
    }
}


r11 | gdinwiddie | 2008-02-01 11:27:20 -0500 (Fri, 01 Feb 2008) | 2 lines

Now our thin slice works, but not our StoryTest.

namespace AddressbookWin
{
    public class Addressbook
    {
        List<Address> AddressList = new List<Address>();

        public void Add(Address toInsert)
        {
            throw new NotImplementedException();
        }

        public List<Address> AllAddresses()
        {
            return AddressList;
        }
    }
}


r12 | gdinwiddie | 2008-02-01 11:35:36 -0500 (Fri, 01 Feb 2008) | 3 lines

After implementing the first slice, the rest of the UserStory is easy to implement.

        public void Add(Address toInsert)
        {
            AddressList.Add(toInsert);
        }


r13 | gdinwiddie | 2008-02-01 12:00:14 -0500 (Fri, 01 Feb 2008) | 5 lines

A little work to ensure the Addressbook class is robust. We don't want client code modifying our addressbook without our knowledge, so we've converted to an interface and return a readonly collection.

        [Test]
        public void EnsureThatAddressbookCantBeAccidentallyModified()
        {
            Address toInsert = new Address();
            it.Add(toInsert);
            IList<Address> retrieved = it.AllAddresses();
            try
            {
                retrieved.Clear();
            }
            catch (Exception)
            {
            }
            IList<Address> retrievedAgain = it.AllAddresses();
            Assert.That(retrievedAgain, Has.Member(toInsert));
        }

        public IList<Address> AllAddresses()
        {
            return AddressList.AsReadOnly();
        }