Test-driven development (TDD) is a common agile software development technique. Although the name implies that TDD is a testing technique, it is actually a programming practice that has both specification and validation aspects. With TDD, you specify your software in detail on a just-in-time (JIT) basis via executable tests that are run in a regression manner to confirm that the system works to your current understanding of what your stakeholders require. Agile teams have been very successful applying TDD in practice, although a recent study has shown that teams are struggling to scale TDD to more complex situations. This is because TDD, by itself, doesn't scaleyou need to apply other techniques to address the concerns that TDD doesn't cover.
TDD is the combination of test-first development (TFD) and refactoring. With TFD, you write a single test and then you write just enough software to fulfill that test. You can do TFD at the requirements level by writing a single customer test, the equivalent of a function test or acceptance test in the traditional world, and at the design level with developer tests. Refactoring is a technique where you make a small change to your existing code to improve its design without changing its semantics. Examples of refactorings include moving an operation up the inheritance hierarchy and renaming an operation in application source code; aligning fields and applying a consistent font on user interfaces; and renaming a column or splitting a table in a relational database. I have an overview of TDD at www.agiledata.org/essays/tdd.html and the Jolt Award winning book Test Driven Development: A Practical Guide (Prentice Hall, 2003) by Dave Astels is an even more detailed resource.
Figure 1 depicts the process of implementing a new feature via a test-driven approach. It starts when you pull a new feature off of your existing work stack to implement. You begin by formulating a customer test that specifies one aspect of the detailed requirements for that feature. For example, let's assume that you are implementing the functional to calculate the discount for an order and that you already have basic order-handling functionality in place. The first customer test that you would implement might explore what to do when a discount shouldn't be calculated; in this case the customer test would involve a customer who is not "elite" making a small order. To implement this functionality, you might first write a developer test that specified that the discount on an order with no items in it was zero and the supporting production code to do that calculation. Once that ran successfully, you'd add a test to verify that the discount for an order with a total of $999.99 is also zero, reflecting the business rule as it was described to you by your stakeholders. You'd then add a test validating that if the order is placed by a customer with a bad payment history, the discount is zero, and so on until you've got the "when not to give a discount" logic implemented. The next step in implementing the discounting functionality would be to write a customer test that specified what to do when a good customer places an order between $1000 and $5000 (give them a 1 percent discount) and sufficient developer tests to drive the implementation of the source code. You would proceed iteratively, writing sufficient customer tests and the supporting developer tests and production code to implement the complete feature. In parallel to this test-first programming, you would refactor any relevant production code that you find that wasn't the best design possible for what you were currently implementing. Once the feature is implemented, you'd pull another off the stack and begin again.