h2. A Simple Stack - The First Specification h3. Where do we start? We want to start off with the simplest thing that we can specify, just as we would in TDD. Remember that in spite of some important (if subtle) distinctions, BDD is TDD at its core. As discussed in the Overview, a common first specification is that a new stack should be empty, and we typically verify this by checking that its size is 0. This simplicity is what we're looking for, but we want to avoid the "size" question. Well, let's think about how a client might use a stack: #example usage stack.pop unless stack.empty? So let's start with "A new stack should be empty". h3. Create a spec file Start by creating a file called stack_spec.rb in /projects/ruby/stack/. You can use any filename and directory you wish, but the following will assume these names and locations. h3. Add a context Add the following to stack_spec.rb context "A new stack" do end A context represents an object (or set of objects) in a known state. This is the same concept as a fixture in xUnit. For each known state from which we wish to specify behaviour, we'll add a new context. To see what damage we've done so far, open a command shell, cd to /projects/ruby/stack/, and enter the following:
$ spec stack_spec.rb
You should see output similar to this
Finished in 6.9e-05 seconds

1 context, 0 specifications, 0 failures
The spec command is installed when you install rSpec. It requires (using the ruby require method) any file(s) (or all of the files in a directory) that you specify on the command line. The context method accepts a name ("An empty stack") and a block (do ... end) and processes everything inside the block. Since there's nothing in this block to process, we discover that rSpec processed 1 context, but 0 specifications. You may wonder why bother with the name? Try adding the -f s option to the command
$ spec stack_spec.rb -f s

A new stack

Finished in 6.9e-05 seconds

1 context, 0 specifications, 0 failures
The option -f s is a shortcut for --format specdoc, which tells rSpec to print out the names of all the contexts and specifications as they are run. As we progress, you'll see how choosing these names carefully not only documents things well inside the spec files, but also in generated output. h3. Add a specification The spec that we are starting with is "A new stack should be empty". So far we have the context, "A new stack", but we need the specification, "should be empty" : context "A new stack" do specify "should be empty" do end end
$ spec stack_spec.rb -f s

A new stack
- should be empty

Finished in 0.000188 seconds

1 context, 1 specification, 0 failures
As you can see, this reads like a specification. This is just like the output you get from running jUnit tests through agiledox. Agiledox was an important part of the inception of BDD, so we built it right into rspec. Of course, we haven't actually written any code in the specification, so we need to add some. Just as we do in TDD, we're going to add one little bit at a time - just enough to produce a failed expectation - followed by just enough code to meet the expectation. Let's start by creating the new stack. context "A new stack" do setup do @stack = Stack.new end specify "should be empty" do end end Introducing a setup here may seem a bit premature to some, but we know what the context is here: it's a new stack. We want to express that context in the setup method to clearly define it as the starting state for each of the specifications within this context.
$ spec stack_spec.rb -f s

A new stack
- should be empty (FAILED - 1)

1)
NameError in 'A new stack should be empty'
uninitialized constant Stack
./stack_spec.rb:3:in `setup'

Finished in 0.00031 seconds

1 context, 1 specification, 1 failure
This tells us that there is a NameError, that it can't find the name Stack, and that the problem is in the setup block of this context. To resolve this, create a new file named stack.rb and add the following: class Stack end Then require that file in stack_spec.rb: require 'stack' context "A new stack" do ... and run the specs.
$ spec stack_spec.rb -f s

A new stack
- should be empty

Finished in 0.000229 seconds

1 context, 1 specification, 0 failures
We're getting closer, but we still need more to exercise this specification. context "A new stack" do setup do @stack = Stack.new end specify "should be empty" do @stack.should_be_empty end end
$ spec stack_spec.rb -f s

A new stack
- should be empty (FAILED - 1)

1)
NoMethodError in 'A new stack should be empty'
undefined method `empty?' for #
./stack_spec.rb:8:in `should be empty'

Finished in 0.000361 seconds

1 context, 1 specification, 1 failure
In this case, rspec interprets the message should_be_empty, and sends empty? to stack. This will work for any predicate (ruby methods that end with a "?" and return boolean). You can think of NameError as the equivalent of a compilation failure in java or C#. Stack doesn't respond to the empty? message ... yet. class Stack def empty? end end
$ spec stack_spec.rb -f s

A new stack
- should be empty (FAILED - 1)

1)
ExpectationNotMetError in 'A new stack should be empty'
Stack # should be empty
./stack_spec.rb:8:in `should be empty'

Finished in 0.000389 seconds

1 context, 1 specification, 1 failure
... and NOW we get a failing expectation. "Stack #<Stack:0x36e60c> should be empty" is the detail of this ExpectationNotMetError. The expectation is that empty? should return true, but it does not. Let's make it so ... class Stack def empty? true end end
spec stack_spec.rb -f s

A new stack
- should be empty

Finished in 0.000256 seconds

1 context, 1 specification, 0 failures
And thus we have completed our first specification: "A new stack should be empty" If you've never had any experience, returning true from empty? may seem a bit strange. Even if you have, you may not feel that the cycle is complete yet. "Test-Code-Refactor", right? Or rather "Spec-Code-Refactor". At this point, we should refactor to eliminate duplication, and you might argue that there is duplication between the spec expecting true and the method returning true. The problem right now is that we have no specs with expectations of the stack being anything other than empty. Any conditional logic we might implement here would be adding new behaviour. Refactoring is defined as changing structure without changing behaviour. And so, as much as we'd like to change true to something more useful, we need to take that urge and recognize that, in this case, we have a deficiency in our specifications - not in the code. Next