Ian Bicking: the old part of his blog

Behavior Driven Programming

I listened to this podcast with Dave Astels about Behavior Driven Development and RSpec, a system for Ruby (the tutorial is probably the best place to start looking at it). He's also simultaneously developing the system for Smalltalk, I believe.

In the podcast Python came up briefly, and a question about whether you could do the same thing in Python. From a quick look, I would say no -- it involves injecting methods into Object, which you cannot do in Python. I assume there are other aspects to the system, but I don't understand them all.

Here's a basic example from the tutorial:

rob = Robot.new

rob.should_not_be_nil
rob.should_be_kind_of Robot
rob.x_coordinate.should_equal 0
rob.y_coordinate.should_equal 0
rob.location.should_equal [0,0]

rob.move_north(1).should_equal [0, 1]
rob.move_south(5).should_equal [0, -4]
rob.move_east(10).should_equal [10, -4]
rob.move_west(5).should_equal [5, -4]

While you can't implement .should_equal in Python, this is trivially translatable to doctest:

>>> rob = Robot()
>>> rob is None
False
>>> isinstance(rob, Robot)
True
>>> rob.x_coordinate, rob.y_coordinate
(0, 0)
>>> rob.location
(0, 0)
>>> rob.move_north()
>>> rob.location
(0, 1)
>>> rob.move_south(5)
>>> rob.location
(0, -4)
>>> rob.move_east(10)
>>> rob.location
(10, -4)
>>> rob.move_west(5)
>>> rob.location
(5, -4)

(I translated the move_* method to Python style, where actions like this typically return None. Update: I realize I didn't explain what this does -- at its simplest you can put that sample interaction into a text file, then run a command that replays that file and checks that it is accurate)

I think doctest exemplifies this idea of "behavior driven programming," though it's usually referred to as "documentation driven programming" here.

RSpec also adds specifications, which are just groupings, xUnit-style TestCases. Nothing special there. I know how to write functions and factor code already, thank you.

So, the advantages of RSpec over doctest? Please note them in the comments, I'm not really seeing anything. Doctest can be a nuisance sometimes because of an explicitness that can lead to fragility -- doctest pays attention to everything and it's not that hard to turn that off, whereas RSpec is more selective. But I don't know if that makes RSpec better.

The advantages of doctest over RSpec?

Doctest is not without its flaws, though I would say most of those flaws are small usability aspects -- many of which were fixed in Python 2.4, but turned off by default. Doctest also gets used in inappropriate places sometimes, but I think that's mostly to avoid the needless complexity of unittest.TestCase. Using a good test runner I feel much less of a desire to use doctests unless I really am telling a story of some sort.

I think doctest is a great example of what Python gets right and Smalltalk-style systems get wrong. Doctest is just a tool. It's not a framework. You don't subclass anything. In Smalltalk you have this nice GUI IDE that rewards subclassing, and conflates the class hierarchy with other kinds of organization, and punishes procedural programming when it takes the form of disassociated actions. In Smalltalk it seems like everything turns into a framework, whether you like it or not.

Doctest of course implements classes for internal use. Classes are a good way to express lots of things. But they aren't a very good way to share. And so doctest doesn't encourage you to use any of those classes unless you are customizing the system in some way. Customizing the system and using the system are entirely separate (a notable difference from xUnit and RSpec). Doctest is just plain good design.

And Behavior Driven Programming? Yep, that's a good idea, so a shout out to Tim Peters for contributing that to the Python community years ago.

Created 23 Mar '06
Modified 23 Mar '06

Comments:

Huh. "Behavior Driven"? Hey, I hear those Ruby people are good at marketing, so maybe they needed a new name for unittesting? Maybe that's not a bad thing. RSpec seems like .. exactly .. py.test? I can't find the blog article mentioned (A New Look at Test Driven Development), but is there some aversion to "assert" in tests that I'm not aware of? Doctest is even nicer, when that's all you need, but other than the Ruby habit of using words to mean builtin things (DSL, righto) x.should_equal 0 => assert x == 0, right? I thought there must be something more I was missing, but here's the full list of expectations that I can find in the source doc on the RSpec site:

should_raise -> assert py.test.raises
should_not_raise -> assert not py.test.raises
should_be_empty -> assert len(x) == 0 # couple options here, depending on what this means
should_be_false -> assert x is False
should_be_nil -> assert x is None
should_be_same_as -> assert x is y
should_be_true -> assert x is True
should_equal -> assert x == y
should_include -> assert x in y
should_match -> (not sure what this ruby means)

and _not_ versions of the same.

Clearly, the whole point of this is a test assertion DSL? Agree with you that creating this by putting methods onto Object feels much less acceptable to my non-ruby/smalltalk mind. But ok, maybe someday I'll go the rest of the way in this DSL fervor.

# Luke Opperman

Apparently they aren't willing to hack the language interpreter a little to improve their testing? py.test does some cleverness to reinterpret assert statements and capture subexpressions, and in the process solves the problem that a whole series of assertEqual/etc methods are created to solve. Doctest solves it in a slightly different way, by basically turning everything into assertEqual (though the fact that is less general can be a problem).

RSpec is doing something else besides this, I think. The tutorial isn't completely up-to-date, so now you can actually do x.should.be.foo which I think is entirely equivalent to assert x.foo?. Since these transformations are generally pretty easy and obvious (and they are supposed to be easy and obvious), I'm not sure what the advantage of the RSpec style is. Maybe if I saw his presentation.

I do think it is or can be correct that "Behavior Driven Programming" is different than just "Test Driven" or unit testing. There really is something different to starting with doctest than with starting with unittest or py.test. It's closer to acceptance testing; acceptance testing of the API, I guess. Unit testing tends to be more focused on testing the internals. By emphasizing a story, doctest is encouraging you to talk about what things look like from the outside. I expect that the style RSpec is implementing is intended to do the same. But creating a DSL for that seems like exactly the wrong thing, because you aren't describing something that anyone can use. When talking about an API, you should be using the API, nothing more.

# Ian Bicking

Ahh, true - I tend to write my tests as "this is how the API should work", so maybe I'm just an early adopter of "Behavior Driven Testing"? :) Although, I thought I was just doing good Test Driven Development... but my point is, TDD freaks people out almost as much as Pair Programming, so if someone wants to try a new name, I'm ok with that.

# Luke Opperman

After watching the Google TechTalk video ( http://video.google.com/videoplay?docid=8135690990081075324&pl=true ) on this I think something like this would be a nice thing in Python. Sure it's really just TDD, but it's changing the semantics to encourage good TDD practices instead of just a verification mechanism.

# Steve

Sure it's really just TDD, but it's changing the semantics to encourage good TDD practices instead of just a verification mechanism.

I think you hit the nail on the head there. I've messed around with both TDD and BDD in Ruby, and, as you'd expect, neither seems to do anything that the other can't do.

BDD just uses different language in an attempt to encourage good testing practices. It's the kind of thing that's easy to downplay, but of course the only difference between any two general programming languages are the things that their syntaxes encourage and discourage.

# Peter