I was running through a group talk I do at the recent Philly CodeCamp (which was a huge success by the way, special thanks to Brian Donahue for doing the heavy lifting in organizing an ALT.NET track) and we were ping pong pairing on the well known bank example. The first story we covered looked like this:
Deposit Funds
As an account holder, I want to deposit funds into my account, so that I can save funds for a rainy day.
Criteria
When depositing funds into an account, the balance should be incremented by the amount deposited.
So someone or another mentioned the whole "simplest thing possible" ethos of pair programming and Agile at large. I have to say I agree, but to a point. For example, this specification would pass (using the "specs" base class I recently published on Google Code):
using NUnit.Framework;
using XEVA.Framework.Specs;
namespace Banking.Model
{
[TestFixture]
public class When_depositing_funds_into_an_account : Spec
{
private Account _account;
protected override void Before_each_spec()
{
_account = new Account(0M);
}
[Test]
public void Increase_the_account_balance_by_the_amount_deposited()
{
_account.Deposit(20M);
Assert.AreEqual(20M, _account.Balance);
}
}
public class Account
{
private decimal _balance;
public Account(decimal balance)
{
_balance = balance;
}
public decimal Balance
{
get { return _balance; }
}
public void Deposit(decimal amount)
{
_balance = amount;
}
}
}
The problem with this is that the spec (remember: not test) oversimplifies things. It doesn't express enough value in and of itself. It's a very test-driven approach as opposed to a behavior-driven approach. When possible, I think BDD specifications should resemble the acceptance criteria attached to the story over leaving a breadcrumb trail of "the dead simplest possible thing" design.
Instead of writing two specs, I'd choose one to that illustrates that two deposits to an account are additive:
using NUnit.Framework;
using XEVA.Framework.Specs;
namespace Banking.Model
{
[TestFixture]
public class When_depositing_funds_into_an_account : Spec
{
private Account _account;
protected override void Before_each_spec()
{
_account = new Account(0M);
}
[Test]
public void Increase_the_account_balance_by_the_amount_deposited()
{
_account.Deposit(20M);
Assert.AreEqual(20M, _account.Balance);
_account.Deposit(10M);
Assert.AreEqual(30M, _account.Balance);
}
}
public class Account
{
private decimal _balance;
public Account(decimal balance)
{
_balance = balance;
}
public decimal Balance
{
get { return _balance; }
}
public void Deposit(decimal amount)
{
_balance += amount;
}
}
}
This shouldn't come as a revelation to anyone, but I think it's extremely important that we represent how we'd cover these problems in the real world. To me, a specification is a whole concept in and of itself. Even though they are extremely granular, specifications express how the software implements business value. Leaving a wake of half-implemented tests in order to adhere to some kind of arbitrary "single expectation/assertion" rule is far, far less important than authoring meaningful and conceptually integral specifications.