"As a long-serving test-driven developer, I want to understand how behavior-driven development differs, so that I can decide whether or not to join the revolution."
Introduction
A couple of years ago I was developing medical imaging software for diagnostic radiologists. The field exhibited an archetypal divide between non-coding experts who knew lots about the domain, and developers who did not. In the language of classical XP, we were coding from requirements supplied by a customer. Without a map, we explored many aspects of the customer/testing/developer relationship:
- the importance of a ubiquitous language spoken across the whole team
- the connection between a spec and an executable test
- the automation of customer-written acceptance tests
Very near the end of that time, I attended XP Day 5 in London. Whilst there I talked some with Elizabeth Keogh, who was assisting Dan North with the birth of Behavior Driven Development. It sounded fascinating and very relevant.
But it became less of an immediate concern, because soon after I changed jobs. I moved to a new language and test suite in the context of a (simpler) domain and a culture where the devs were encouraged to internalize the expert/customer role.
Now, over the past couple of years the BDD meme and tools have been surging in popularity. Meanwhile I'm working well in a classical Test-Driven Development place some indeterminate distance away, catching occasional glimpses in my peripheral vision of what looks like a pleasant enough image in a slightly warped fun-house mirror, and trying to work out how the reflection differs.
A couple of months back Tom ten Thij gave a great BDD introduction at our local Ruby Users Group. But he had to handle heckling from many of the audience who met the following spec:
- Given
- I'm a software developer who loves TDD and communicating well with customers,
- When
- I hear a talk on BDD that seems to promise exactly what I get from TDD and good customer communication,
- Then
- I find myself wondering exactly what all the fuss is about.
I took the question back to Liz, and she had interesting things to say. I hope sometime she may write them up properly herself, but in the meantime she said it would be fine if I wrote something. So, here's an [edited] transcript of parts of the e-mail conversation we had.
Context
Ant:
Yesterday at our local Ruby User Group here in Edinburgh we had a
presentation on RSpec and on BDD in general. The talk presented BDD from
first principles rather than assuming previous TDD experience. The
audience was mostly TDD old-timers, so a lot of the talk seemed like an
very familiar tale told in slightly different dialect.
Ant:
Our fuzzy consensus was acceptance that BDD is a nicer syntax; its
language is a better fit for the good things that test/spec-driven
development tries to do and hence the way it should be done from first
principles - but, is the change worth it for TDD die-hards who have
already completely internalized xUnit? What else do we get?
Ant:
We realized none of us had ever come across a talk or article
evangelising BDD which particularly spoke to us; for example, by a
someone who'd been very dedicated acolyte of TDD and had many wonderful
things to report once they'd learned to Behave.
Liz:
I can't think of anything off the top of my head that addresses TDD gurus,
which is awful, because we should have sorted this out ages ago. Let me see
if I can help - please let me know if this is any use!
Ant:
I'll devil's advocate some.
Personally, I'm a big believer in the importance of language [and]
I'm simply not experienced enough to make any claim of
having seen it all before [but]
I'll proxy for my peers who have been doing/teaching TDD for donkey's
years.
Philosophy
Liz:
BDD isn't just about reworking the syntax of testing, though it started out
that way. It's evolved into something more; a language of examples that's
relevant at both a unit and system level, which allows better communication
with the business, and less redundant code.
Liz:
DDD [Domain-Driven Design] provides the nouns, verbs and adjectives,
and BDD provides all the other
words which let you make sentences out of them. (Dan North, Eric Evans and I
were chatting about this at OOPSLA; I can't remember the exact words they
used, but this is close.)
Liz:
It's all about the language. You don't really get anything else... but
language is pretty powerful! Changing the words we use can change the way we
think, and we don't have to change our technology to do it.
Territorial dispute
Liz:
TDD is mostly about development - about taking a specification or acceptance
criteria, at either a system or unit level, and making the associated tests
pass. BDD assumes that it's not easy to get the right acceptance criteria in
the first place, so the language - including the famous "should" - is designed
to allow the original assumptions about the behaviour of the system to be
questioned; to have discussions about that behaviour without having to
translate from the technical world, either in speech (which helps us devs
talk to BAs and the business) or mentally (which helps us realise when we
need to ask a question). Of course, this is very closely aligned with DDD.
It's also why I prefer "behaviour" to "spec"; because it's easier to question
behaviour than a specification.
Ant:
I disagree with this characterization. Particularly at the lower levels
where the developer may be doing more of the work than the domain
expert, traditional TDD is very much about exploring and evolving the
behaviour of a system, one test at a time, asking questions rather than
blindly coding up an existing design or specification.
I completely buy the idea that the language of BDD helps make the
openness to change explicit, though.
Liz:
The best TDDers do BDD already, at least at a unit level. It's possible that
they don't think of elements of code as stakeholders, though; at least, I
haven't found any native TDDers with that mindset. It changes the names of
classes, at the very least. I am also aware that some die-hard, expert TDDers
concentrate more on the technology when they're teaching than the
conversations around tests.
Ant:
It helps my understanding if I consider BDD to
be taking a concept of TDD that fits particularly well with driving from
behaviors, and turning the dials on that concept up to ten. If not plain
wrong, this characterization may play well with an old guard XP/TDD
target audience.
Frameworks
Liz:
First off, let me put to rest something really important:
You can do BDD in xUnit.
Ant:
would you go so far as to say that if you aren't
starting from scratch there may not sufficient advantage to switch from
xUnit to a more well-Behaved framework even if one falls for BDD in a
big way?
Liz:
Don't use JBehave. It's a great teaching tool, but sucks IRL. I tend to use
JUnit. JBehave 2.0 should (when it eventually comes out) be easier. It's
going to be more like RSpec's plain text story runner, and consist of a lot
of JUnit exensions.
Talking to business
Liz:
[BDD offers] a language of examples that's relevant at both a unit
and system level, which allows better communication with the business
We use the word 'scenario' or 'example' because you don't go to the business
and say 'Give me an acceptance test'. You say 'Give me an example' or 'Give
me a scenario where that happens'.
It becomes easier to maintain acceptance tests - and they become
human-readable!
Ant:
Would you venture further? Non-developers can edit the acceptance test?
Non-developer can write the test when paired with developer as
translator? Non-developer can even write the test unambiguously without
direct developer assistance, given some further translation layer like a
plain-text story runner?
Liz:
Absolutely. This has worked very well with our QAs.
Ant:
The last was a feature of the talk I attended. It drew attention from
the old TDD guard, who were all very XP-story-focussed and some of whom
had developed such translation layers themselves. It provoked both a
"these BDD people sure have the right idea" response as well as a "and
it's an idea we had a while back, and is separable from the question of
changing terminology at the xUnit level" one.
Liz:
Good. "Should" is trivial compared with the given / when / then language.
Ant:
Partly putting words in their mouths, and partly exploring something I
thought might be interesting: if you do end up with another layer
between the non-developer and the runnable test/behavior/spec - and our
experience is that perhaps this is worthwhile - then some of the
communication advantages of using BDD at the bottom layer are diluted.
If you have to use a translation
layer like a story runner to pre-process a more plain text description
of behavior, then using BDD terminology in the target code doesn't help
you so much with respect to communicating with the non-developer,
because they don't see that code, they see the source story. At that
point the BDD communication argument perhaps reduces to the DDD one
(albeit one I buy) that using business language as much as possible in
the code itself is worthwhile even when only the developer sees it.
Ant:
I guess I was wondering whether you'd challenge the pre-condition - can
one make behaviors expressed in the production code language
sufficiently like plain English to be writeable by the non-developer?
(Perhaps it depends on the language?)
Liz:
RSpec's stories now work in plain English. We hope JBehave 2.0's will be the
same. It's a nice little given / when / then DSL.
Ant:
I guess my point was that some long-time TDDers have so internalized
some of the translations from dev-centric terminology to plain English,
that they might consider a test very easy to read way before the
non-developers do. But purging such language habits would probably ease
indigestion nevertheless. And if BDD terminology becomes the standard,
there's also a communication benefit amongst developers - the next
wave may well see TestCase/test/assert as strange dinosaur language.
Outside-in
Liz:
[The BDD spec] leads to the first piece of code which the human stakeholders need,
which is the UI - the mechanism through which users (or 3rd party apps, say)
interact with our system, and through which behaviour is perceived. So this
is how we develop with BDD - from the outside-in.
Ant:
This was a part of BDD missing from the talk we heard, and it sounds
interesting.
I enjoyed your real life example.
[Re TDD] with "the YAGNI of outside-in" - whilst YAGNI itself is a TDD
cornerstone, I haven't heard the tenet that therefore one should always
start with the user interface layer.
Liz:
If you don't start with the interface, how do you know that you need it? How
do you know that the stakeholder code will be able to use what you're writing
in the way that you're writing it? You can pretend that you already have the
code, work out how you're going to use it, then code the real thing later -
this is exactly what we do with TDD anyway! Now we're taking that pattern
into production code too.
Code has needs too!
Liz:
We use the same language of examples, to describe behaviour that provides
some benefit, at both a unit and a system level. The stakeholder at a unit
level becomes the other piece of code which needs that behaviour - so unless
we have some code already, we can't start.
Ant:
This analogy between human and code clients is not one I'd heard
advanced quite so strongly in this context - is it important to BDD?
Liz:
Yes!
Ant:
[In TDD] this definitely isn't an angle I've
seen pushed hard. Trying to make client use of the new code readable,
sure, but not the kind of anthropomorphism you're advocating.
Liz:
I'm not thinking of the stakeholders as human, as such, though it's a good
analogy - code changes just as much as people do! BDD is very need-driven.
Why are you coding that? Who's it for? What is going to benefit from that
line of code? It changes the names of classes, at the very least.
[...]
We've started naming things based on the responsibility to its stakeholder,
or its job, instead of the design pattern. As an example, we've started
calling repositories after the plurals of whatever you get out of them - so,
"Puzzles" for my sudoku creator, as opposed to
"PuzzleRepository".
Given/Then, When
Ant:
Random aside... is it perhaps the case that most mocking layers for
xUnits (at least in languages that don't love closures) make given/when/then
expression harder? It seems that expressing the expectations of mocks
should go in the "then" section, but they usually assume they'll be set
up before you execute the code under test within the "when" section.
Liz:
Check this new toy out: Mockito
My friend Szczepan Faber made it, based on EasyMock. It doesn't make you set
up any expectations unless you need something to return (eg: given three
fridges in stock). Instead, you get to ask it afterwards whether the
interactions needed happened. No finishedRecording(), and it'll automatically
return null, 0 etc. if you haven't set up a particular stub.
Conclusion
Well, I hope you found that as interesting as I did.I'd love it if the "TDD old guard" (who I've quite possibly
misrepresented) could comment below to take the conversation further...
Tags: software_development