BDD vs TDD – How Behavior-Driven Development is a Form of Test-Driven Development

bdddevelopment-processmethodologytdd

When looking and Behavior-Driven development, "Behavior-driven development is an extension of test-driven development" (From Wikipedia). Researching around various articles, behavior-driven development is a form of TDD which is writing unit tests. At the same time, BDD focuses on business value, but not all modules provide direct business value on their own. (This is often true in embedded software development.) Business value is only added when separate modules are used together. So it seems contradictory that behavior tests focused on business requirements can be implemented using unit tests testing individual modules.

Does behavior driven development work on the level of writing individual unit tests for a single unit. Or is it actually more closely related to integration testing, crossing over multiple units?

Put in an a example: How do you write behavior driven "unit tests" for low-level modules such as a memory access module or a component driver if that low-level component doesn't provide any business value on its own?

Then, in the other direction, how do you write behavior driven "unit tests" for a single high-level component if the business requirement can only be validated when integrating low-level modules and can't be validated using mocks of the low-level module?

Related Question, but doesn't fully distinguish BDD from TDD: How to use unit tests when using BDD?

Best Answer

TDD was invented by people who focused on behavior. Those people saw it take off and become popular. Once it was popular, it got into the hands of people who thought about TDD structurally. The TDD structural people wouldn't let you create a class without wrapping it in tests. They created ways to subvert accessibility modifiers, so they could mock all the other classes this class collaborated with, regardless of whether the collaborators were slow or non-deterministic. Any test that could be run under a unit test framework was called a unit test. The unit was always a single class.

BDD was invented by people who focused on behavior. Those people were hoping that if they explained the same idea better, people wouldn't corrupt it as they did the idea behind TDD. It seems to have worked better this time. Though, its existence doesn't seem to stop the structural TDD people.

If you disagree with the above, that's fine. That's simply how it looks to me. Keep it in mind, though, because it's the lens that I look through when dealing with the issues you bring up.

If you want to be one of the behavior people, then I ask you to consider refactoring. Every good test tests via some interface and expects some behavior. That test should pass even if the implementation behind the interface completely changes. That only works if you don't insist on testing private functions, abstracted objects/classes, or any other implementation details you only see when you reach behind the abstraction of the interface. Test that way, and refactoring only makes tests fail when the interface is being redesigned.

If you want to be one of the structural people, I don't really have a good answer for you. BDD doesn't make sense when you work and think this way. It was designed not to.

To behavior people, TDD and BDD are the same idea explained differently. A unit is not some structure of your language. It's a boundary you draw. Inside which you expect the code to be deterministic and fast so that your test is reliable and performant. It doesn't matter how many methods are called or objects get referenced. Those are implementation details. It doesn't matter how high or low level the code is. Those are implementation details.

Given a list of requirements, and a list of untested public methods, the behavior people will focus on the requirements. Just like private methods can get their code coverage by being called by a public method, so too can public methods that are hanging off of objects which are abstracted away by other objects.

In a sufficiently complex codebase, it can be useful to have tested units within tested units for no better reason than to explain code with the example a test provides. But this is a subtle trap. This is a test that exists for the sake of the code. Not a business need. Satisfy the business need some other way, and then the code, and its test, no longer need to exist. Tests, ones that keep unneeded code around because people don't understand that the test is not in direct support of a requirement, can end up preventing needed refactoring.

Business value comes from features. Not code. If I could have all the features and none of the code, I'd love it. Because features are what pay the bills. Not code. When you write tests, focus on the features you need. Not the code you have.

Related Topic