h2. A Simple Stack - Organization and Documentation - IN PROGRESS - DISREGARD THIS PAGE h3. Spec organization Let's take a look at the spec thus far... require File.dirname(__FILE__) + "/stack" context "An empty stack" do setup do @stack = Stack.new end specify "should keep its mouth shut when you send it 'push'" do lambda { @stack.push Object.new }.should_not_raise Exception end specify "should raise a StackUnderflowError when you send it 'top'" do lambda { @stack.top }.should_raise StackUnderflowError end end context "A stack with one item" do setup do @stack = Stack.new @stack.push "one item" end specify "should return top when sent the 'top' message" do @stack.top.should_equal "one item" end end ..and the result of executing them. First in default mode...
$ spec stack_spec.rb ... Finished in 0.000439 seconds 2 contexts, 3 specifications, 0 failures...and then in verbose mode...
$ spec stack_spec.rb -v An empty stack - should keep its mouth shut when you send it 'push' - should raise a StackUnderflowError when you send it 'top' A stack with one item - should return top when you send it 'top' Finished in 0.000533 seconds 2 contexts, 3 specifications, 0 failuresWe've got two contexts. In both we exercise the 'top' message, but we only exercise 'push' in one. This is a wonderful aspect of organizing the specs this way. By looking at the contexts themselves, or by looking at the generated output, and by having only one setup for each context (a guideline often suggested for xUnit, but violated just as often), we can clearly see the imbalance in what messages we're specifying in the different contexts. So now, in addition to using the principle of the simplest thing to help us decide what to specify next, we've also got this feedback that tells us that we have yet to specify how a stack with one item should respond to the 'push' message. Using both tools to guide us, that is an obvious next choice. context "A stack with one item" do setup do @stack = Stack.new @stack.push "one item" end specify "should keep its mouth shut when you send it 'push'" do lambda { @stack.push Object.new }.should_not_raise Exception end specify "should return top when you send it 'top'" do @stack.top.should_equal "one item" end end To add that, I actually copied the spec from the "new stack" context. Cut and paste? Blasphemy! Exact duplication? More blasphemy! Well, it is blasphemy if you think of these structures as tests, or even as code. Yes, they are executable. Yes, they are code - sort of. But they also serve other very important purpose - they are documentation. h3. Specs as documentation The whole structure of contexts and specifications was deliberately chosen to feel less like the tests that we're all used to seeing in xUnit frameworks so that we wouldn't think of specs in the same way as we do tests. So while we might think of them as executable, we don't want to think of them as code. We talk about tests as documentation in TDD as well. But I can tell you that when I've tried to read them as such and found myself searching through hierarchies to find the setup for a test that was failing, my eyes have just glazed over and I've ended up looking directly at the code. Serving as documentation requires of these executable specifications that they be simple, clear and easy to understand. All the context you need to understand the test should be in one place. Also, the perception that "all duplication is evil" is based on industry-wide experience in which changes that needed to happen to duplicated code didn't happen everywhere it should have. In the case of specifications, that doesn't really fly because each specification is autonomous. If the rules change for a message in a given context, then you'd only want to make the change in that context. So we've now got 4 specifications, 2 each in 2 contexts. Run the specs...
$ spec stack_spec.rb -v An empty stack - should keep its mouth shut when you send it 'push' - should raise a StackUnderflowError when you send it 'top' A stack with one item - should keep its mouth shut when you send it 'push' - should return top when you send it 'top' Finished in 0.000657 seconds 2 contexts, 4 specifications, 0 failures...and there are no failures. So in this case, since 'push' results in the same behavior for an empty stack and a one-item stack, we don't have anything additional to implement. Previous | Next