4

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?

3
  • 6
    TDD is not and never was about unit testing. TDD is about driving the development process through tests. Note: "tests", not "unit tests". There is no "U" in "TDD". Aug 11, 2021 at 16:00
  • @JörgWMittag Thanks for the clarification. I think the wikipedia entry for BDD is incomplete and a little misleading "Test-driven development is a software-development methodology which essentially states that for each unit of software, a software developer must define: a test set for the unit first..." Aug 11, 2021 at 16:48
  • "TDD means (again, kind of obvious) letting your tests drive your development (and your design). You can do that with unit tests, functional tests and acceptance tests. Usually, you use all three." softwareengineering.stackexchange.com/questions/59928/… Aug 11, 2021 at 16:49

4 Answers 4

9

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.

7
  • wouldn't corrupt it like they did TDD: TDD did not corrupt anything. People who misunderstand TDD corrupted the "behavioral aspect" you mentioned. Many "respected" doers published books to explain TDD "to corrupt it" because they simply did not read Kent Beck's original book. Aug 12, 2021 at 6:04
  • It wouldn't be the first time a group of developers decided on their own to change the meaning of some word or concept. REST is the most egregious example I can think of, and there are many others. There is actually a comment on one of Roy Fielding's blog posts that claims REST no longer means what Roy says it means, because the "consensus" has evolved to mean something else. As if that somehow carries more weight than the guy who invented the term. Aug 12, 2021 at 13:03
  • From here: “Common REST” is all about using PUT and DELETE and organizing your data into CRUD-based interfaces. Common REST is winning mindshare over REST as you defined it. In an environment where the number of links defines authority that means Common REST IS REST. Aug 12, 2021 at 13:11
  • To which Roy Fielding replies: There is no such thing as “Common REST” (nor, for that matter, the alternative declarations of “Low REST” and “High REST”). There is just REST. The problem is that various people have described “I am using HTTP” as some sort of style in itself and then used the REST moniker for branding (or excuses) even when they haven’t the slightest idea what it means. The only way to stop people from misusing the term is to make a negative example of them when they do. Aug 12, 2021 at 13:12
  • @RobertHarvey if we're gonna talk about what happened to REST, can you top this? Oh and thanks for the edit. Aug 12, 2021 at 14:17
4

The level of testing (unit, integration, system) and testing strategy (applying TDD and/or BDD) are orthogonal.

Often, TDD is considered at the unit testing level, and sometimes the integration testing level. However, it doesn't have to be. For example, Acceptance Test-Driven Development (ATDD) is at the system and acceptance level. TDD is all about using tests to guide (or drive) the development. You start with failing tests to define what the system should be capable of and then implement what is necessary to make the system achieve that goal.

BDD is an extension to TDD that, like TDD, can be applied to any level. The emphasis on BDD is making the intended behaviors of the system transparent via the tests. Any method of testing can be used to implement TDD, since it's developer-oriented. BDD exposes the tests as a way to specify the behaviors of the system using domain-specific language.

There is an implication that BDD requires TDD, since the behavior-driven tests drive the development. If the tests are written after the code, then one could argue that the development was not driven by the tests. However, one could still write tests in a way that emphasize the behavior of the system even after the code is written.

Since the behaviors captured in BDD tests are often from the user's perspective, they are likely to be system-level tests. However, there's nothing stopping a technical person from using BDD-style tests to capture the behavior of units, or individual classes and methods.

There's also the difference between using a BDD framework and using BDD. Since BDD implies TDD, saying that one uses BDD means that one writes the tests in a behavior-oriented manner before writing the code. However, a test framework doesn't enforce this. One can write BDD-style tests after code has been written, making what they do something that isn't BDD.

All of these decisions are decisions that should be made by the team. Does the team want to use TDD? Does creating BDD-style tests make sense? At what levels should TDD be used? Does it make sense to use a BDD test framework for all levels of testing? Answers to these questions can inform a team in making decisions about their processes and tooling.

1
2

TDD & BDD are very simple topics, yet there is so much confusion around them. Like the fact that BDD some how relates to TDD. It doesn't. They are completely orthogonal. Similar to @VoiceOfUnreason I'd like to reference Dan North's article about how BDD was created and developed - it's worth reading if you want to know the real story.

BDD started as a naming convention for tests. Instead of saying "test this and that" you now say "this works this and that". That's it. Later on the author thought that it was a good idea to extend this to other roles on the team like Business Analysts. But so far it doesn't seem to be working.

TDD is just about the order of writing tests vs writing production code vs refactoring. I covered the difference between TDD & BDD in more details here.

Whether you write a unit test or a more high level test - it can be about the behaviour. Behaviour doesn't mean it's about business or money. If it's a memory management code your test could say "during GC memory is released if object is not referenced" - that's an example of BDD-named test.

6
  • 2
    BDD and TDD are far from orthogonal. They have so many things in common, the most important one being the principal goal: to drive development using tests. I think BDD could be seen as a superset of TDD.
    – ccov77
    Aug 12, 2021 at 7:07
  • @Juan, BDD does not drive development. Dan North was under a lot of influence of TDD at the time and this "BDD" term was a great marketing move. But that's just it - it's a tacky (and imprecise) name. Personally I don't always follow TDD, but I always follow BDD. The latter doesn't require the former. Please read the links that I posted. Aug 12, 2021 at 7:16
  • I did, and he speaks about testing throughout the entire article. To "drive" the development of a project using tests means that tests steer the project towards the right direction. It doesn't matter if those tests are Unit tests, focused on smaller units (like classes), or full blown acceptance tests that focus more on the behavior. The key part is that there is a test guiding the development of the project (development including requirements, coding and maintenance in this case).
    – ccov77
    Aug 12, 2021 at 15:45
  • @Juan, okay, since we're going in circles - let me ask you to show some kind of proof. Give me an example of how BDD steers the development exactly. Here is how TDD drives it: instead of thinking of large problem you split it into smaller steps and implement your classes & methods in small chunks. Now show me an exact way in which BDD drives development. Aug 12, 2021 at 16:10
  • Refer to Wikipedia: en.wikipedia.org/wiki/Behavior-driven_development, more specifically the paragraph: "Principles of BDD" .
    – ccov77
    Aug 12, 2021 at 18:44
1

If you want to understand the history of Behavior Driven Development, you should probably start from the introduction by Dan Terhorst-North, first published in March 2006.

I decided it must be possible to present TDD in a way that gets straight to the good stuff and avoids all the pitfalls.

My response is behaviour-driven development (BDD). It has evolved out of established agile practices and is designed to make them more accessible and effective for teams new to agile software delivery.


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?

These communities didn't use a definition of "unit test" that was clearly in alignment with the definition(s) used by the testing community.

As far as I can tell, Beck's tests have always played a little bit loose with how many "units" are being evaluated. More important was the idea that the testing step be quick, and therefore test design pays attention to isolating tests from one another, and making sure that test execution didn't depend on any elements that were slow or unreliable.

Boris Beizer, in his books on testing, refers to tests with multiple "units" as "component tests".

You also find alternative definitions of unit that emphasize deployment - "unit" might be an entire package, because that's the "physical" thing that you can actually pick up an move around.

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?

You do one of two things - you design your tests with known good components interacting with your low-level test subject, the "unit-test" approach; or you test a cluster of unverified units together.

The nature of the test itself, as far as I can tell, doesn't change very much. You do end up, perhaps, with more "fixture" code than you would need when measuring higher level components.

(As far as I know, none of the characters in these stories are bringing a lot of low-level experience to the table. I'd recommend looking into James Grenning's work


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?

It turns out that nobody is awarding prizes for "unit tests", of any definition.

If your tests catch your mistakes less than a minute after you make them, the label doesn't matter. All you care about are the constraints introduced by that "less than a minute" requirement - you need to be willing to run the tests often, and that means that they have to be reliable, and quick enough to not be a distraction, and ... and ... and....

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.