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();
}