Pluralsight blog Where devs, IT admins & creative pros go for news, tips, videos and more.
Pluralsight + Digital-Tutors - 3,000 tech & creative courses - starting at $29/month Get it now →
October 15, 2012

TDD: Is There Really Any Debate Any Longer?

By

This post is in response to Dr. Dobb’s editor, Andrew Binstock’s recent editorial about the universality of unit testing. I think we all can agree that it’s universally accepted that unit tests are good. But what about Test Driven Development (TDD)?

Promoting the practice of TDD in the JavaScript world has occupied much of my recent professional activity. I have created testdrivenjs.com to do that, and I’m currently authoring a course for Pluralsight on that very subject. The reason I have done all this work is my very strong belief that TDD as a practice has a large amount of value well beyond what most people understand. In fact, I believe that of all the practices made popular by the agile movement, TDD is the most beneficial practice overall. Here are eight reasons why I believe that TDD should be beyond debate.

1. Better Code

Both unit testing and TDD produce better code than not using tests. To unit test a piece of code, you must decouple it from its dependencies so that they can be easily mocked. That process not only helps you write more loosely couple code, but also encourages you to reduce the number of dependencies a class may have. It will address several code smells like too many parameters in methods and constructors, feature envy (a class that uses another class too much), and too many dependencies on other classes.

Beyond that though, the process of TDD causes an incremental approach to building code. You will follow a red (failed unit test), green (passing unit test), refactor cycle many times while building a class. That opportunity to frequently refactor your code will encourage you to spend more time refactoring it. That means more time renaming identifiers to more intuitive names, more time spent thinking about the readability of your code, more time spent deciding if a given method is too large or too small, more time thinking about if this class should be split up into more than one class.

2. Easier Coding

Because you build a class incrementally in TDD, not in one shot, you only have to worry about one responsibility at a time. You don’t have to keep in your head all the things you want the class to do, all the dependencies it needs to work with, all the inputs and outputs that might break the class, all the conditions you need to check for to make sure that arguments are valid. With TDD you can take that one step at a time. And as you build each case, it often leads you to the next case, and the next one.

Although you can follow the same process without writing your unit tests first, testing after the fact doesn’t inherently encourage you to do this.

3. Solution Triangulation

Because in TDD you incrementally build an algorithm, you can essentially triangulate in to the correct solution. The bowling game kata by the illustrious Uncle Bob (google it) is a fantastic example of this. You can start with an algorithm that solves just a simple case of the problem, then slowly add more cases and refine the solution until it’s 100% correct. That makes it much easier to solve a problem than just trying to get it right the first time in one big shot.

Again test-afterwards doesn’t prevent you from following this process, but it doesn’t encourage it either. TDD practically forces you to follow this process.

4. Coding from the Client’s Viewpoint

With TDD, you write the test first. A test is a client of an object. It calls the class, consumes its return value, and must know if the process was successful. That process makes you think about the object from the client’s point of view, so you will be encouraged to make the object a good abstraction of the responsibilities that the class has. You will make its interface more clean and concise, and the method names will be more explanatory than otherwise.

5. More time thinking up front

We all know that too much up front design is a bad thing, but so is too little. On a micro scale, TDD makes you think more about the design of a particular class. You have to think about what dependencies it needs, what methods it will have, how it will interact with its collaborators, what kinds of values its methods will return. Because you can’t write a test until you have some concept of how you will call a method, and what that method will return, and how you will check its correctness, you will be prone to building better classes that are highly cohesive and follow the Single Responsibility Principle. This point is closely related to the previous one.

Testing afterwards doesn’t encourage this practice. Again you’re free to do it, but as a practice you aren’t encouraged to think about a class because you can just start coding and worry about that stuff as the class gets built.

6. Better Unit Test Coverage

Although unit testing means writing unit tests to cover your code, there are so many variations that code can take. I’m not talking about just the number of branches through code, but all the variations in code. If you take in a string parameter, that string can be null/undefined, empty, or contain a value; it can be overly long, it can contain special characters. Does your code truly handle all these cases? If you test after, the quantity of tests you write will trend lower because you will simply be verifying current behavior. (See the next point)

With test-after, your tests don’t help you decide what code to write, so therefore, variations on tests become less interesting and less a part of the core process

7. Sloth And the Temptation to Skip Unit Tests when Under Pressure

As humans we are lazy. It’s wired into us. If you don’t believe this, read Thinking Fast and Slow by Daniel Kahneman. When doing a task, the part of the task where we are most likely to cut corners is the end. We’re almost finished; we want to be finished; we want to check it off. So if testing is a distinct activity at the end of our current mini-task, we are far more likely to cut corners and write fewer and less comprehensive tests.

Also, when we test afterwards, because it’s at the end of the process, if we are under pressure from our bosses, product managers, or clients, or just our timeline itself, then one of the most tempting corners to cut is the testing. If you’ve already seen that your code is working correctly, then skipping tests for that code is extremely tempting when the pressure to ship is applied. With TDD, that is impossible. You can’t write code without testing it first, so you can’t skip out on the tests regardless of the pressure.

8. No Extra Code

With TDD, the process is to just do the minimum amount of coding to make the test pass. This becomes an art form since you must learn to write appropriate tests that cause you to write sufficient code. But when your tests are all passing, and you’ve cleaned up the code to be as well-factored as you want it, then you’re done. The temptation to write more code that handles some possible future requirement (YAGNI) is diminished since you’re only writing tests to satisfy known requirements of the code.

Testing afterwards doesn’t guide how you write your code, with the exception of making it testable. So it gives you no encouragement to write only the code that you need and no more.

TL;DR

In summary, the difference between TDD and simply writing unit tests is big. Don’t let the commonalities make you think that you’re getting pretty much all the benefits because you write unit tests. If you’re not doing TDD you’re missing out on so much more.

About the Author

began his love of programming on an Apple III in BASIC. Although his preferred language is JavaScript, he has worked professionally with just about every major Microsoft language. He is currently a consultant and full time author for Pluralsight. Joe has always had a strong interest in education, and has worked both full and part time as a technical teacher for over ten years. He is a frequent blogger and speaker, organizer of ng-conf, the AngularJS conference (www.ng-conf.org), and a panelist on the JavaScript Jabber podcast (http://javascriptjabber.com/)


Discussion