Test Driving Code:
Top-Down or Bottom-Up ?

Last updated: october 27 2004

sven.gorts@refactoring.be

Abstract: Some time ago a collegue asked wether to test-drive top-down or bottom-up. Surprised both by the question as well as my answer I started thinking.

Things Have Changed

Astonished by hearing myself recommend to test-drive top-down I started thinking. Now I used to favor a bottom-up development style but ever since the adoption of TDD I find myself developing top-down. At least most of the time: when an implementation gets though I tend to switch gears. Time for a brief comparison of both styles.

Test Driving Top-Down

When test driving top-down the tests I write are usually limited to testing the higher level operations. Each of the higher level methods tends to have multiple tests. Lower level methods, mostly private ones, tend to have none.

What I like about test-driving top-down is that the resulting tests verify the abstraction rather than the implementation. Even though this may not seem important at first it really pays off during later refactorings, in a reduced number of tests that need to be updated.

But test-driving top-down also has its downside. It's not always easy to find an incremental approach to get a feature implemented. And trying to do too much it may be more difficult to get the tests to green quickly. Occasionally we encounter a combinatorial explosion with many combinations of parameters to be tested.

Basically, test-driving top-down comes down to getting started. However, if the problem is too big it's easy to get stuck.

Test Driving Bottom-Up

When test-driving bottom-up I write tests for the lower level operations first and compose the higer level operations later. Each method ends up with at least one tests, however close to none of the methods tend to be private.

Often I choose for bottom-up development because the overall solution isn't quite clear. Developing bottom-up then allows me to develop building blocks I can use later to compose a solution. In other words, I start to write tests for smaller methods that can be tested individually and defer test driving the larger ones until the smaller parts have matured.

Disadvantage of test-driving bottom-up is that the tests become much more tied to the implementation details of the code we're test-driving. Should we need to refactor the implementation later, many of the tests will inevitably have to be updated as well. Additionally, the resulting abstraction may reflect some of our implementation choices.

Switching Gears

Neither style excludes the other. In fact, one of the fun parts about test-driving code is that you can rapidly switch between them. Most of the time I find myself test-driving using a mixture of top-down and bottom-up development. As each style has different characteristics I switch between them depending upon the coding problem at hand. I usually find myself test-driving top-down when I have a pretty clear idea of what the abstraction I'm test-driving will look like and I feel that working top-down helps me to keep focus on the level of abstraction I want.

Things, however, aren't always that clear. When I find myself struggling to get the red bar to green quickly I switch back to bottom-up development. The reason I do this is because I want to gain control of the situation quickly. Once the bar is back to green I switch back to top-down development.

TDD top-downTDD bottom-up
< = >
give me abstractiongive me control

Conclusion

Due to the adoption of test driven development my development style has shifted in favor of top-down development. Even though test-driving top-down works as the better default this certainly isn't a hard rule: For though problems I manytimes switch gears to bottom-up development. Once I get a hold on the problem refactoring helps me to upgrade the tests to the appropriate level of abstraction.