TDD vs. BDD or why can’t we all just get along?

Saturday, December 10, 2011

I was listening to another good Hanselminuets podcast - Understanding BDD and NSpec with Matt Florence and Amir Rajan. As always it was a good an informative show. Towards the end of the show one of the interviewees (I think it was Amir) explained why BDD is much better than TDD…

 

For those of you who does not know what I’m talking about – TDD is Test Driven Development (or design) and BDD is Behavior Driven Development. TDD is more “low level” talking about unit tests and integration tests and how to write code that test code while BDD is about specifications and behaviors.

In TDD we talk about Arrange Act Assert while in BDD we have Given When Then

Each methodology has its own frameworks that help write and run tests (or specifications) that make sure our code does what it meant to do.

Am I the only one that see a resemblance?

You might argue that one is better than the other till you blue in the face it does not change the fact that they are two sides of the same coin.

There are differences – BDD is more “high level” dealing with specifications and was intended to be used by domain experts as well as developers – although I’m yet to find the product manager that would write specifications. On the other side TDD is developer oriented and intended to be used by people who write and read code daily.

But regardless of these differences I don’t really see a reason why you should choose one over the other. I confess I use TDD and unit/integration tests in most of my projects but I do have several projects where I use both. I like the fact that I can write unit tests as well as specifications – because I need to test both. I also find myself using unit testing framework (i.e. NUnit) to write more “BDD’ish” kind of tests.

And so I don’t really understand why one is better than the other. Mainly because I prefer to use both – when possible.

And if you never tried BDD (or TDD) before I suggest you do and see for yourself.

 

Happy Coding…

3 comments

  1. Hi Dror, maybe I can help clarify some things :-).  

    I completely agree with you, I really don't like the delineation between TDD and BDD either.  I think this delineation has come to existence to show that writing your tests first isn't enough...and instead of elaborating on that and clarifying TDD, we as a community have decided to create yet another three letter acronym...BDD.

    At the end of the day all I want is a valuable test, ie a test that communicates intent, tells a good story, and protects the next developer that needs to maintain the system.  

    Here is how I ensure a valuable test:

    - I don't delineate between integration and unit test anymore. 

    Why write a "unit" test if it doesn't tell a good story for the next developer? 

    Is it worth mocking out all external dependencies to a point where a given component looses its meaning? 

    We tend to do that when we label a test as a "unit" test.

    - I start with a "coarse grained" test, which is a test that gives enough context to tell a good story.  It may use many components together, which is perfectly okay.

    - When I see myself doing the same setup over and over again for a "coarse grained" test, at this point I decide whether it's okay to create a "finer grained" test for one of the internal components, sometimes it's the right choice, sometimes I keep things at the "coarse grained" level because it communicates intent better.  I keep things relentlessly DRY throughout this process...moving common setup to shared methods.

    - I try not to test the "how", I only test the "what".  I try to avoid testing "structure", I imply structure while directly testing the "what".

    Here are some examples of some NOT so great "BDD style" tests: 

    "when creating a blog post, it gets inserted into the database" (testing the how)

    "given a car, it has 4 wheels" (testing structure)

    "a blog post has a title and a body" (testing structure)

    Here are the same tests that are more valuable:

    "when creating a blog post, it is visible on the home page" (no mention of a database, a more coarse grained test because it incorporates retrieval of a blog post)

    "a car is driveable if it has 4 wheels" (implies structure through some behavior we can assert)

    "a blog is valid if it has a title" (implies structure through some validation behavior)

    "retrieving a blog post shows the title and body" (still trying to avoid testing structure directly)

    "listing blogs shows a summary of the blog, which is the first 1000 characters of the body" (testing listing of blog posts, introducing summary and body)

    Here are some BDD tests I've written using NSpec they may help clarify these points:

    - A kata about a piece trying to move around a map that has obstacles: https://gist.github.com/1115867

    - A kata describing how one poker hand beats another: https://gist.github.com/1412830

    - Reference implementation for a dynamic ASP.NET MVC Application (Blog Engine): http://bit.ly/uJ0Sqm

    - More Complex reference implementation for a dynamic ASP.NET MVC Application (Video Game Trading Website): http://bit.ly/rSN1so

    - Test suite for a dynamic ORM: http://bit.ly/rVjaLV

    Hope that helps!

    ReplyDelete
  2. I totally disagree with the notion that you should mix integration tests with unit tests in the same suite. To me, these are two very different things.


    Unit tests should describe a single interface for a single module (unit) of code. Typically that means a class, a small class-oriented service, or a module. The unit tests should describe, document, and exercise the public-facing API (not the internal, the "how").

    The course grained tests you're talking about are integration tests, and they should be kept separate, 1) because they are not useful for documenting the interface of a specific unit. 2) They often require a whole lot more implicit environment to be available (to coordinate interactions with other units), for example. Good unit tests should be able to run in isolation, even in parallel.

    Keeping these ideas clearly separated is an important key to developing maintainable test suites. Blurring them is a recipe for test suite fragility, because every refactor implies gigantic rippling changes to the entire test suite, whereas a refactor to one module is almost guaranteed to have no effect on another units properly written unit tests.

    Refactors might invalidate certain integration path tests, but they should almost never invalidate another module's unit tests.

    ReplyDelete
  3. Arik,

    I didn't mean that both types of test should be in the same suite - for the exact reasons you've stated.
    All I wrote is that you should use both in your project (not project file)

    ReplyDelete

Newer Older
Related Posts Plugin for WordPress, Blogger...