The Way I've Come To See BDD
Over the last year and a half, I have been moving from TDD to BDD. For those who don't know, this is not a tool or technology switch so much as it is a mental paradigm shift. Luckily, I didn't work in a lot of shops that ever did a lot of unit testing (testing units of code after you write them). Most often, we'd be under time crunches that forced PMs to make the decision to forego unit testing in order to meet a hard deadline that they got from some estimation crystal ball (that's a rant for another time). The positive outcome was that I didn't get sucked into many of the pitfalls that plagues those who came from shops that did more unit testing. However, I was still convinced it was about testing.
It's not about testing. As Dave Astels would say, "TDD is not aboot testing, it's aboot design." That's a good way to say it (without the Canadian accent, of course). TDD is about driving the design of your code based on what the consumer of that code specifies. The problem is, most people start with what the consumer needs and try and break that into small pieces of engineering and then test that. BDD takes a different approach.
BDD (for me) starts and ends with user voice. If you can't describe it in the user's language, you should stop and think about whether or not you're over engineering. It may start with a simple, "A user should be able to log in.", and that can spawn several scenarios. What should happen if the user's username is bad? their password? What happens if they DON'T have a username and password? Should we allow the user to "stay logged in"? Once the user successfully logs in, where should they be taken? All these things are tenets of good requirements gathering, and they become specifications for building what the consumer wants/needs. After much reading, discussing and practicing, I have seen two major styles of BDD. I've come to call them "Concentric Circle BDD" and "Context Specification".
Concentric Circle BDD
Concentric Circle BDD starts with a plain English scenario, written exactly as a user or BA might write it. The generally accepted pattern for these scenarios is the "Given, When, Then" syntax. For instance, "Given that I have a valid username and password, when I enter them in the login screen, I should be successfully logged in”. In this more "traditional" BDD style, you use a tool like Cucumber, JBehave or NBehave to write code that will pull in that text and parse it and execute it in a observable, provable way. Once you have a failing scenario, you drop into the inner concentric circle and begin writing more focused specifications, that observe small, independent pieces of that scenario. Once each inner piece is working, the scenario should be proven as well. Concentric Circle BDD is, I believe, what Dan North had in mind when he introduced BDD. If you've done any BDD with rSpec and Cucumber, using the two together leads to this type of BDD.
Using this outside-in circular approach, lets developers start right where the user leaves off. Take the story and scenarios and make them executable. Easy enough, and it overcomes one of the biggest barriers for those new to TDD and that is where to start. The main drawback, is that once developers get to the "inner" concentric circle, they might become lazy about keeping specs in the user's voice. It's easy to write a spec called "should_call_login_method_on_authentication_service" instead of calling it, "should_authenticate_user_against_registered_users". Staying in user voice makes it easier to ask a user how something in the system SHOULD behave, when you are unsure.
Context Specification
The Context Specification style of BDD foregoes the need for the Scenarios written in plain English and the need to write the wrapper code that will ensure they are executable. Instead it takes those scenarios into the "inner" circle directly. They break the scenarios into contexts and observations that look an awful lot like the Arrange/Act/Assert used in TDD (Which is all Given/When/Then is if you think about it). By breaking the larger scenario into smaller pieces, it's a bit easier to write the specifications that will prove the system does what it should, but it can be a bit of a disconnect for developers compared to the "outer" circle of the Concentric Circle method that takes the scenarios right out of the user's mouth. Some developers break the specifications into "Bullet Point" specifications, to make them easier to translate directly into Contexts and Observations. These specs are usually kept closer to user's voice when they are written, generally because they will be the artifacts presented to the user or Business Analyst when communicating what you have made the system do so far. It can, however, confuse those new to BDD when trying to figure out where to start.
A Third Approach
I might be late to the party and perhaps everyone else was doing this from the start, but I've recently noticed that our development team's Context Specification specs have evolved into almost a hybrid of the Concentric Circle and Context Specification approach. I don't think we moved there consciously, but I've noticed the BDD really getting easier and moving faster. Also, I've noticed a marked improvement in the quality and simplicity of our code. We are less and less often wandering down a path toward "over-engineering " a solution to a given problem. This approach rids us of having to write a bunch of code to parse and execute "User English" scenarios, but helps us to get started with an outside-in approach to specifying what the system should do. Here's how it works:
Everything is written as a spec, but the first spec almost mirrors the user's scenario. This "outer" circle is almost completely an "interaction" or "integration" specification. Most of what you see in the context is mocked objects, and each observation is an observation about how each piece interacts. Almost every observation is an "AssertWasCalled" (that I have extended with a ShouldRecieveCall method) call. To me, this represents the outer ring of the system. Then for every "interaction" observation, there is an opportunity to pull a concrete implementation of that mocked object (or subsystem) and specify how it should behave. This inside circle may be a subsystem that requires another interaction type specification, that observes how each piece of that subsystem interacts with every other piece in it. Eventually, you will move down to a very small independently observable specification. In the inner circle, you deal more with concrete classes. Specifying the implementation details of those behaviors. In the next few weeks, I will be blogging more about the samples of this approach. Leave some comments and let me know what you think. Am I on to something or just ON something?