Archive for the ‘TDD’ Category

Behavior-Driven Development

Friday, March 2nd, 2007

Behavior-Driven Development (BDD) has been hanging around, tugging gently at my brain for a few months now. Like most interesting ideas, it’s only a small evolutionary step from its predecessor, TDD. But I think it’s a subtly powerful concept, one that I’m starting to pay more and more attention to in my work. The introduction sums up the evolutionary shift at a personal level:

1. The developer starts writing unit tests around their code using a test framework like JUnit.
2. As the body of tests increases the developer begins to enjoy a strongly increased sense of confidence in their work.
3. At some point the developer has the insight (or are shown) that writing the tests before writing the code, helps them focus on only writing the code that they need.
4. The developer also notices that when they return to some code that they haven’t seen for a while, the tests serve to document how the code works.
5. A point of revelation occurs when the developer realises that writing tests in this way helps them to “discover” the API to their code. TDD has now become a design process.
6. Expertise in TDD begins to dawn at the point where the developer realizes that TDD is not about testing, it is about defining behaviour.
7. Behaviour is about the interactions between components of the system and so the use of mocking is a fundamental to advanced TDD.

Word.

Is Test-Driven Development Overrated?

Saturday, October 21st, 2006

The (Misguided?) Faith in Unit Tests stands as a rare counterpoint to the flurry of pro-TDDers (one of whom is me). The author makes some great points; but they have more to do with incorrect applications of TDD than flaws in the approach itself.

Let’s separate the technical design of software applications into two layers: high-level, which is an outline of all the working parts and how they fit together; and low-level, which is a description of the details of each part. A high-level design of a shopping cart, for example, might be something like: “Products represent items the user can buy, and belong to one Category. Cart is a container for CartItems, each of which specifies a quantity and is linked to a Product.” Design decisions you would make at this level are things like: can a Product can belong to more than one Category? Or: is multicurrency implemented through its own class, or just a currency_type enumerated field alongside any price field on an object?

The low-level design is a description of the interface to each object. For example, how does one add items to the cart: cart.addItem(item, or cart.getItems().add(item), or item.addToCart(cart)? Do you copy the price of the Product onto the CartItem when it is added to the cart, to prevent updates to the Product from affecting the customer’s checkout price? Does the shipping calculator connect to the shipping carrier’s API every time it needs to calculate a cart’s shipping price, or does it cache a local table of weights and shipping values to avoid slowdown from the network cost of the transaction? Most of these decisions affect either the internal implementation of the objects, or the APIs they export.

Tests, and unit tests in particular, help somewhat with low-level design, but not at all with high-level design. The latter is informed by the needs of the application. High-level design defines the mapping between software and the real world. Tests have a limited scope: their whole universe is the application, and anything beyond the boundaries of the code cannot be addressed. (It should be noted that business logic tests are one attempt to bridge this gap; we use this for some of our apps, but it isn’t appropriate in all situations.)

Furthermore, TDD is not so much a way to help create your design as a way to enforce it. Yes, writing the unit test first is a good way to discipline yourself to create a clean, straightforward, and orthogonal API for any given class, and I make this very point repeatedly in my screencast on test-driven development. But this is only half the story, maybe less. TDD’s real benefit over the long term is documenting and enforcing the interface and functionality of an object. A unit test says: “This is how the object is supposed to work, and I can prove it: just run me.”

So if someone new is working on the project, they can find out how multicurrency support is handled or what you have to do to add an item to a shopping cart by looking at the tests. Better yet, if they change an object’s implementation in a way that is inconsistent with the low-level design - for example, tinkering with the pricing code for products without properly handling multicurrency - this will break the tests, thus forcing the developer to confront the fact that they have a misperception about the design.

Where is the right place for the high-level design to live? For this I don’t think we have any solution other than traditional documentation. To some degree a good UI communicates many aspects of the design: for example, a breadcrumb trail shows the relationship between the primary objects (e.g. Store > Category > Product). But there’s usually still plenty of behind-the-scenes design that can’t be communicated this way. For that, some text descriptions entered into a wiki, along with some diagrams showing class or table relationships, is still your best bet.

People never liked technical documentation because the bulk of its maintenance is related to low-level design. If you’re documenting that this object has these ten methods and this first method is named this and takes these three arguments, each which have this type - well, that’s going to get out of date quickly. It’s a pain to maintain which means it won’t be up to date, and when it’s not up to date no one uses it and thus it becomes worthless. Unit tests free you from this painful cycle. (If you’re making a complex, publicly-used support library, reference documentation generated from the real code with something like RDoc should complement the tests.)

Cut out documentation of low-level design, though, and you’re left with much less documentation to write. I suspect most good technical designs - and by “good” I mean “free of excessive complexity” - could be summarized in a few paragraphs and a diagram or two, backed up by a larger document to cover domain-specific knowledge for the data tracked by the app.