"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."
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.
- I'm a software developer who loves TDD and communicating well with customers,
- I hear a talk on BDD that seems to promise exactly what I get from TDD and good customer communication,
- 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.
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.
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.
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.
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.
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.
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?
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.
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
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.
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.
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...