I followed a chain of blog entries and comments where people were discussing the merits of TDD; those lead to the essay Test-induced design damage by DHH of Rails fame. It’s a good essay. And just to be clear, by “good” I don’t necessarily mean “I agree with it.” What I mean is that it makes interesting points that will cause your neurons to fire. At least, it did mine. DHH is a smart guy, and for heaven’s sake, he invented Rails, so when he has something to say about how Rails is best tested, it’s worth reading.
DHH argues that slavish application of TDD can cause twisted designs, code that is less clear than it would be otherwise. I know that’s true for me – I’ve created less readable code in the name of making it testable. What I don’t know is how much that’s TDD’s fault, and how much is just me not seeing a better way to to it.
As an example of code distorting the design, DHH links to a video by the late Jim Weirich (you know, the Rake guy), in which Jim demonstrates the application of hexagonal architecture to Rails. It’s a good video, especially in conjunction with DHH’s criticism. And yes, “good” here means the same thing as it did above.
In the course of the demonstration, Jim showed how he writes rspec tests (sorry, “specifications”). I saw a wonderful style of rspec I hadn’t seen before. It looks like this:
This is an excerpt from stack_spec.rb, which is part of Jim’s rspec-given gem. If you’ve ever worked with Cucumber, you’ll recognize this style. Here’s a piece of a cucumber test so you can see how similar they are:
Background:
Given the test server is started
And a successful connection
Scenario: No argument
When the client successfully asks for help
Then the server should return a list of commands
Scenario: Known command
When the client successfully asks for help for "NOOP"
Then the server should return help for "NOOP"
Scenario: Unknown command
When the client successfully asks for help for "FOO"
Then the server should return no help for "FOO"
At first, Jim’s “given” syntax looks like it’s just renaming some
methods: let
becomes Given
, before(:each)
becomes When
, and
specify
or it
becomes Then
, but there’s more going on. One
thing I like is the addition of And
: This is like a Then
, but it
does not redo the setup. It inherits the state left by the previous
Then
or And
. That lets you do this (stolen from the gem’s
README):
Without rspec-given, you would it like this:
Now, this kind of test, where you repeatedly poke at an object and check the results of the accumulated state changes, isn’t my favorite way of writing tests. I usually prefer that each test checks just one thing. But there are some tests that are easier to write and read in this rolling style. Jim’s syntax is nice for those.
Jim’s syntax pleases me very much. Also, the README is very well written, with just the right level of detail. I’m going to give rspec-given a try on my next project.