Archive for 2007
What is Behavior Driven Development (BDD)?
According to behaviour-driven.org BDD was born from NLP experiments (astonishingly, nlp.com mention NLP and hypnosis in the one sentence!).
It is a combination of TDD and DDD. Here are some of my attempts at describing BDD:
A process for describing the behavior of a system that is understood by both technologists and non-technologists.
DDD applied to the domain of TDD.
The concept of a ‘vocabulary for describing behavior’ is at its core, but interestingly the behavior-driven site falls short of defining the vocabulary (note the incomplete links at the base of this page).
BDD is strongly linked to TDD as tests are often used to describe the features, or behaviors, of a system. Consequently, part of BDD involves writing tests using the vocabulary.
That’s the driving principle behind RSpec. Test::Unit is perfectly fine for defining tests, but it doesn’t employ a BDD vocabulary.
RSpec does, and it throws in a side order of Mock and Stub support often needed for TDD. As it happens this support is virtually identical to that provided by Mocha.
Take your pick, the support both provide is particularly cool as you can Mock and Stub methods of any object, sure you can create Mock and Stub objects (in a fashion that I’ve been used to with EasyMock and JMock) but the object doesn’t have to be a Mock or Stub in order to Mock or Stub a method invocation.
To highlight this here’s an RSpec demonstration applied to a banking model;
describe Account do before(:all) do @@amount = 100 end before(:each) do @account = Account.new end it 'should withdraw cash via a Transaction' do Transaction.should_receive(:deduct).with(@account, @@amount) @account.withdraw @@amount end it 'should log unsuccessful operation when withdraw is unsuccessful' do Transaction.should_receive(:deduct).with(@account, @@amount).and_raise(InsufficientFundsException) @account.should_receive(:log_outcome).with(:unsuccessful) @account.withdraw(@@amount).should_raise(InsufficientFundsException) end end
Pretty cool huh?! This mocks two methods in a manner that can’t be easily repeated with Java:
- A class method, Transaction.deduct
- An instance method of a real object, @account.log_outcome
Take a look at the recently tweaked documentation on the RSpec site for info on it’s other features (like partial argument matches), and more generally its implementation of a BDD vocabulary.
Why use RSpec?
One of my initial encounters with RSpec was via a blog critising BDD that goes so far as to suggest RSpec is pronounced ‘arse peck’, so I’ve been skeptical from early on. I have been particularly cosy with XUnit tests, a mock framework and consistent test method naming conventions as a way of specifying behaviors, so I didn’t feel the urge to change. Test::Unit and Mocha can pretty much yield the same result - what did I have to gain?
Still, this ol’ school approach requires some discipline and custromizations to read like a BDD vocabulary. RSpec, on the other hand, encourages codification of tests in a consistent BDD vocabulary. But hey, if you don’t intend on using the vocabulary for communications between roles (and that’s a substantial change) your missing the major benefits of BDD - so why bother?
Nonetheless, it’s worthy of consideration.
With its subtle shift in the XUnit language I believe it produces more readable tests. That coupled with its nice mock and stub support and the potential benefits of a shared language for describing behavior - the pro’s start outweighing the con’s.
I’ve read some BDD proponents claim they sought out BDD as a solution to tests that are tightly coupled to source, hindering refactoring and causing a maintenance burden.
I fail to see how BDD helps here; this is a natural consequence of writing unit tests. For tests to do their thing, an API under test must be invoked - you can’t avoid that. If you change that API; surprise, you’ve got to change your unit tests! If you’re suffering this affliction maybe you should test against an API few layers up, like HTTP. Personally though I’d be blind without my unit tests, at a minimum they aid design and verify correctness.