I have had some limited success on previous projects with TDD but wanted to try out BDD. It has always been problems when I wanted to tell about TDD to others. Some of them are:
- Where do one start writing tests?
- When do one start writing tests?
- What, in Unit testing, is a unit?
BDD seems to address some of these problems.
Dave Astels has some interesting thoughts about addressed the need for something different than TDD.
It was for me easy to get carry away by the benefits of TDD and the praise it got from the community. Trying to stay sceptic was difficult. Also because when one starts to learn a technique one is missing the details that can ultimately revel some weaknesses. However I still believe that if one does TDD very very well then their are no flaws. But few people do that, me included. Let us take a look at some of the problems with TDD that one can fase:
- Naming of tests
- The focus on interfaces instead of requirements
- The different definitions of Unit in Unit tests
- The lack of understand the difference of state based testing and interaction based testing
- The misleading mixup of mock object, stubs, fakes etc in some of the frameworks
- Where one should start writing tests
- The lack of looking at the requirements and instead focusing on implementation details
In essense what he is saying is that we want to get away from the focus on tests and testing. All to often I find that when one starts with writing tests(before the implementation) the focusing is on the wrong things. It does not matter if it is a list or an array. That is the implementation details that very well can change during the course of the project. We want to focus on functionality as we write code that verifies the production code.
Another problem I often fase when teaching TDD or doing it myself is the lack of focus on the requirements. One starts with a test that is testing some class. Gradually one move "up the layers" to the GUI only to find out that it was not the required functionality. BDD fixes this by starting on the "outer layers".
The naming of the tests are also a problem for most people. I have read and follow the naming convention by Roy Oshrove in his book "Art of unit testing". BDD is in essens the same as TDD if you do TDD very very well. The Java byte code or the C# CRL code is probably somewhat the same. The problem is that most people is not doing it very well. You have to follow the many constraints to arrive at a good TDD practise. As we shall see BDD has a approach that includes these constraints in the method itself. Lets take a look at the first rule for writing good tests, namely the naming.
Naming tests confusions
Starting with TDD one of the first problems you are faced with is the names on your tests. Roy Oshrove suggests that a good name should answer the 3 questions:
- What are you testing?
- What are the scenario you are testing?
- What is the expected result?
By including these elements in the test name one can easily see what is wrong with the application should one test break(given that the test is correct or corse). However I think few people do this.
What I found out after working with BDD is that this aligns perfectly to their Given-When-Then template for writing requirements. Given describes the world in which you are testing, When describes the scenario and Then describes the desired results. The test runners for BDD also allows for nesting of context. That means that one can append more and more context in different scenarios.
It can also easily be combined with the recommended way to write User stories in SCRUM or other Agile methodologies. The user stories have the template "As a <type of user>, I would like to <some funcionality> so that <some benefit>.
The focus on interfaces instead of requirements
This problem is also somewhat tied together with the misunderstanding of what a unit in unit testing is. Often I see that a unit is defined as a method. After one has defined a unit as a method it is off to the races to make it part of something that is highly modular. To make the test go fast one introduces mocks and stubs and in essense end up testing very little of value. One is testing the interface between the modular entity and the rest of the world or that the state of the entity is correct after all the mocks and stubs have returned all that you said they should.
BDD here instead starts from the outer layers and with the context of requirements. One should still mock out external calls that could make the test suite very slow or alter the state of the application in production(such as changing database records). However the internals of the application is more or less left alone intact.
The hassle of refactoring due to tests tied to tightly to implementation details
This relates to the previous point. When all of the tests are at the method level you often end up having test that know to much about the implementation. Testing against the length of an array for example breaks when the implementation if changes to use a hashmap.
BDD does not have this problem in the same extent. The internals are more or less left alone as long as the required functionality if satisfied.
The confusion of where to start writing tests
When starting at the method level it is often very difficult to start writing tests. I find that the tests come more naturally ones I have defined more of the outer context first. BDD supports this by focusing on the outer layers.
It can be a good exercise to turn off the computer and write the BDD specs by hand just looking at the requirements.
Relationship to Domain Driven Design
I find that DDD can be a great tool to use on certain projects. Especially those with a high degree of business rules. The core in a domain driven design architecture should not have any external dependencies. The technology used should limit itself to the programming language and belonging framework of choice, e.g, F# and the .NET framework. This has in my experience been a great place to do BDD as well. Clearly stating the requirements that the core domain should satisfy. Since we are not having any external dependencies in the core domain, the tests will be running fast. And we can also focus more on the domain rules instead of the technology.
We have been adding tests to the outer layers of DDD as well to check the issuing of domain events and relationships to the infrastructure layer for example.