OK, it’s time to get our hands dirty with a little coding. Following
a strong tradition of contrived examples, I’m going to develop a simple
stack class to demonstrate some of the techniques of using executable
specifications to drive the functionality and design of the code base.
For this example I’m going to use NSpec as the specification framework.
We all know what a stack class is, right? It’s sort of a last in, first
out (LIFO) collection.
For the purposes of today, I’m going to say that the interface our stack class will support is as follows:
public
class
Stack<T>
{
public
Stack();
public
void
Clear();
public
bool
Contains(T item);
public T Peek();
public T Pop();
public
void
Push(T item);
// Properties
public
int
Count { get; }
}
Now, clearly we’re not going to win any awards for the class, but it should serve its purpose.
So what are the first things we could say about it? Well, when the
stack is empty, its Count should be zero, and trying to call Pop or
Peek should cause an exception. So how might we write that in an
executable specification? Well, let’s take the first one as an example:
using NSpec.Framework;
namespace Stack.Specs
{
[Context]
public
class
WhenTheStackIsEmpty
{
Stack<int> _stack = new
Stack<int>();
[Specification]
public
void CountShouldBeZero()
{
Specify.That(_stack.Count).ShouldEqual(0);
}
}
}
That’s the simplest way of expressing that piece of behaviour. And
I’ll need to implement just enough to make that code compile, and the
spec fail. Here’s what I’m starting with:
namespace Stack
{
public
class
Stack<T>
{
public
int Count
{
get
{
return -1;
}
}
}
}
And firing up the spec runner, I get:
But making the test pass is trivial, by changing the Count property to:
public
int Count
{
get
{
return 0;
}
}
Don’t cry foul, we’re not done yet. What else did we say about an
empty stack? We said calling pop or peek should cause an exception.
Taking a bit of a short cut, specifying these two bits of behaviour:
[Specification]
public
void PeekShouldThrowException()
{
MethodThatThrows mtt = delegate()
{
_stack.Peek();
};
Specify.ThrownBy(mtt).ShouldBeOfType(typeof(InvalidOperationException));
}
[Specification]
public
void PopShouldThrowException()
{
MethodThatThrows mtt = delegate()
{
_stack.Pop();
};
Specify.ThrownBy(mtt).ShouldBeOfType(typeof(InvalidOperationException));
}
These requirements can easily be met with:
public T Peek()
{
throw
new
InvalidOperationException();
}
public T Pop()
{
throw
new
InvalidOperationException();
}
Now we have 3 passing specs. What else could we say? Well, when the
stack has stuff in it, we’d like the count to be correct, we’d like to
know whether it contains a particular item, we’d like to be able to
clear it, and we’d like things to Pop and Peek in the right order. So
let’s get started by adding a new Context, and a new spec method:
using NSpec.Framework;
namespace Stack.Specs
{
[Context]
public
class
WhenTheStackContainsItems
{
Stack<int> _stack;
[SetUp]
public
void SetUp()
{
_stack = new
Stack<int>();
_stack.Push(0);
_stack.Push(1);
_stack.Push(2);
}
[Specification]
public
void CountShouldBeCorrect()
{
Specify.That(_stack.Count).ShouldEqual(3);
}
}
}
Now we’ve got some work to do. This doesn’t compile because we don’t have a Push method yet, so let’s add one:
using System;
namespace Stack
{
public
class
Stack<T>
{
int _current = -1;
T[] _items = new T[10];
// Snip
public
void Push(T item)
{
_current++;
_items[_current] = item;
}
}
}
But shock! We have a failure.
What went wrong? Oh that’s right, we cheated in our initial
implementation of Count, so now it’s time to do it right. In the spirit
of YAGNI (You Aint Gonna Need It) it’s almost always best to fake a
method until you are forced to do it right.
public
int Count
{
get
{
return _current +1;
}
}
4 green dots and 1 happy camper. So let’s move on to checking whether it contains a certain item:
[Specification]
public
void ShouldNotContain999()
{
Specify.That(_stack.Contains(999)).ShouldBeFalse();
}
[Specification]
public
void ShouldContainZeroOneAndTwo()
{
Specify.That(_stack.Contains(0)).ShouldBeTrue();
Specify.That(_stack.Contains(1)).ShouldBeTrue();
Specify.That(_stack.Contains(2)).ShouldBeTrue();
}
And some implementation:
public
bool Contains(T item)
{
foreach (T t in _items)
{
if (t.Equals(item))
{
return
true;
}
}
return
false;
}
And we’re good, 8 green dots. What next? Let’d add support for clearing:
[Specification]
public
void ClearShouldEmptyTheStack()
{
_stack.Clear();
Specify.That(_stack.Count).ShouldEqual(0);
}
Red dot.
public
void Clear()
{
_current = -1;
}
Green dot. Now on to the fun bits, peeking and popping:
[Specification]
public
void ShouldPopThemInTheRightOrder()
{
Specify.That(_stack.Pop()).ShouldEqual(2);
Specify.That(_stack.Pop()).ShouldEqual(1);
Specify.That(_stack.Pop()).ShouldEqual(0);
}
Again, our earlier short cuts are biting us, red dot. Let’s iron out the wrinkles:
public T Pop()
{
if (_current > -1)
{
T popped = _items[_current];
_current–;
return popped;
}
else
{
throw
new
InvalidOperationException();
}
}
And for the peeking:
[Specification]
public
void PeekShouldReturnTopItem()
{
Specify.That(_stack.Peek()).ShouldEqual(2);
_stack.Pop();
Specify.That(_stack.Peek()).ShouldEqual(1);
_stack.Pop();
Specify.That(_stack.Peek()).ShouldEqual(0);
}
This gives us the same problem as before, so, ironing again:
public T Peek()
{
if (_current > -1)
{
return _items[_current];
}
else
{
throw
new
InvalidOperationException();
}
}
There, 15 shiny green dots. We have a rudimentary stack class with
some pretty sever limitations, but in terms of the specifications,
every time we build this code, every time we add to it, or refactor,
every time we fix a bug, these specifications will be here, ready to be
run. Don’t underestimate the confidence these kind of tests can bring
to your micro ISV. Time is precious, and believe me, writing these
specs once, will pay you back very quickly, as it drastically reduced
repetitive, error prone manual checking later.
