Test-Driven Development (TDD) is a fundamental practice in the Extreme Programming methodology. The concept behind it is that before you write code to meet a requirement, write a test that, when it passes, proves the requirement is met.

For example, let's say you have a User and when that user does something bad, they get locked out of their account for 24 hours, and if they try to log in, they get a message that says that they are locked out.

Using Test-Driven Development, we would first start with a Test. Given the above requirement, our test might look like:

[Test]
public void badUserIsLockedOutAtLogin
{
  User user = new User("Bad User");
  user.MarkAsBadUser();

  Login loginAttempt = user.Login("password");
  Assert.IsFalse(loginAttempt.Successful);
  Assert.Equals("Sorry, you are a bad user and your account is locked", loginAttempt.ErrorMessage);
}

(Quick overview. The [Test] marks this as a Test in C#. The Assert.IsFalse and Assert.Equals are methods that, if fail, cause the entire test to be marked as a fail and display as a red bar if you are using a GUI runner)

Our goal now is to do the simplest thing possible to make this test pass. In fact, in it's current state, the test probably doesn't even compile, because your User object may not have a MarkAsBadUser() method. So you add it.

Once you added the method, you find the test fails because the Login object doesn't take into account if the user is a bad user. So you add that. Then you add the code to produce the error message.

Now, you've added the code for the feature, and your test passes. Not only do you have the feature added, but you have a test that proves it, and the same test acts as a safety net for regression testing as you make future modifications to the code.

The key points to TDD are:

The entire process is also called Red, Green, Refactor named after the pretty red and green colors that the GUI test runners (like JUnit and NUnit) display with failing and passing tests.

Some other key points:

  • Test Everything that could possible break - Generally this means that simple getters and setters aren't tested unless there is actual business logic in them
  • Refactor only on a green bar - these Unit Tests should run at 100% green at all times. If you are changing code not related to a failing test on a red bar, you don't have your safety net

There are frameworks available for just about any language one can think of, including C#, Java, Javascript, PL/SQL, Ruby, Python, Perl, C++ and many, many others (Including DUnit, the Delphi version of the xUnit framework (thanks StrawberryFrog!)).


Resources:
  • http://www.objectmentor.com/writeUps/TestDrivenDevelopment - an overview of TDD by Object Mentor
  • http://www.testdriven.com - A site dedicated to TDD resources
  • http://en.wikipedia.org/wiki/Test_driven_development - Wikipedia article on TDD

Test-driven development (TDD) is a programming methodology that's gained a lot of mindshare in the last few years. Although it was first described as part of Extreme Programming (XP), it's taken on a life of its own as one of the better aspects of XP. As with all buzzwords, caution is advised to all developers and suggestible IT managers. Test-driven development is a legitimate technique, but always remember there are no silver bullets.

Process

The methodology is a simple series of steps.

  1. Plan the mechanics of how one small bit of functionality should work.
  2. Write a test that verifies that bit.
  3. Write code to satisfy the test and no more.
  4. Repeat.

Benefits

TDD encourages test completeness.
Good unit tests are the backbone of any large software project. Without automated regression tests, a project of sufficient size will eventually collapse under its own weight. There is no better way to ensure good test coverage than to proclaim that all code will be written to satisfy an already-existing test.

TDD gives you a new perspective on the design process.
When you write a test first it makes you think concretely about certain aspects of how your code works. These aspects aren't necessarily more important, but they can reveal inconsistencies before they become bugs.

TDD documents the design process.
A test is an explicit declaration of a programmer's intent. Flowcharts, technical specs and even code comments all have their place in documentation, but they won't tell you when their assumptions no longer hold true.

TDD formalizes what you would be doing by hand.
Writing tests incurs some overhead, but realistically any programmer has to test all their code before committing it anyway. The overhead is quickly amortized over the life of the project.

Drawbacks

TDD ignores top-down development
TDD keeps the developer focused on the details, making it easy to lose the forest for the trees. If some of the high-level details change, it could render all of the low-level work useless.

TDD is only as good as the test harness
Although any testing framework will make it possible to test logical units of your code, often the ideal test involves complex semantics that aren't readily available. To get around this you may write sloppy tests, or use an inferior architecture just to make testing work better.

Testing some things is a waste of time
Unit tests generally follow the law of diminishing returns. The first one verifies that the environment is set up correctly and everything compiles. A few more can offer basic coverage of all your code. Beyond that you start to devolve into minutiae. So at some point it's more time effective to debug known problems than to preemptively write tests for every possible issue.

One way isn't right for all situations
Sometimes writing a test first results in premature design decisions that have to be scrapped down the line. There's no magic formula here, all you can do is keep an open mind and constantly re-evaluate your methodologies.

Writing Good Tests

Writing unit tests is an art unto itself. The entire process is subjective. Whereas code must do what it is meant to do, tests are selective observers. What they observe is what the programmer deems important; they are a window into his psyche.

Writing good tests requires experience. First ask yourself:

  • What is most likely to go wrong?
  • What is most likely to change?
  • What small errors would precipitate a business crisis?
  • Which design decisions are fundamental to the project?
  • Which are incidental?

Munge all that in your head, and mix in some boilerplate guidelines:

  • Each test should test only one thing
  • Tests should be simple and obvious—no chance for a bug in the test
  • Every line of program code should have at least one test that runs it

The key to effective test-driven development is fluency in your chosen testing environment. You need to practice writing tests and making the aforementioned judgments. Over time you will see that some tests end up being more trouble than they are worth, requiring frequent fixes to keep them valid. Other tests end up alerting you to unanticipated problems that you never would have expected. Once you are fluent, the testing overhead diminishes and you start to see great returns. Test-driven development forces the developer to sharpen his testing skills. This is far more beneficial than strict adherence to a rigid methodology per se.

Log in or register to write something here or to contact authors.