Why do programmers hate writing unit tests? Why do they hate even more writing unit tests before coding? You don’t have to answer. I’ve already heard the excuses. These are rhetorical questions. I have a theory, however, what the real reasons are.
Most software developers have never seriously tried test-first. Or if they tried it, they did not do so in a supportive environment where they could learn what they were doing. But more likely the former. So they come up with excuses: “We don’t have time to write unit tests.” Or: “Unit tests can’t bullet-proof your code.” They’re reacting out of fear, not out of empowerment, trying to find reasons for being miserable, rather than making their lives better. The neat thing is that occasionally you’ll find one of these developers who, when you face him with the facts, nearly trips over his own feed backpedalling. That’s fun to watch. And he does this all without ever taking back his opinion of test-first. That’s wicked philosophical.
As Kent Beck explains in Test Driven Development By Example, test-first comprises three steps:
- Red — Write a test that expresses how you’ll use the code and what you need it to do. This test will fail, producing a red bar on many UI’s.
- Green — Write enough code to get the test to pass, but no more. If you need more code, for example, to check for errors, first write another test to demonstrate that feature. For now, just write enough code to get the test to pass.
- Refactor — Clean up the code to remove redundancy and improve the design. Then re-run the tests to make sure you didn’t break anything.
Repeat these until you’re done. It’s an incredibly simple procedure. So why do developers fear it? Because it requires them to make a fundamental paradigm shift in the way they develop software.
How do you solve a software problem? How do they teach you to handle it in school? What’s the first thing you do? You think about how to solve it. You ask, “What code will I write to generate a solution?” But that’s backward. The first thing you should be doing— In fact, this is what they say in school, too, though in my experience it’s paid more lip-service than actual service— The first thing you ask is not “What code will I write?” The first thing you ask is “How will I know that I’ve solved the problem?”
We’re taught to assume we already know how to tell whether our solution works. It’s a non-question. Like indecency, we’ll know it when we see it. We believe we don’t actually need to think, before we write our code, about what it needs to do. This belief is so deeply ingrained, it’s difficult for most of us to change. Of course, for a trouble-maker like me, the change was nothing more than a sensible experiment that worked.
For anyone who can make that leap, here are some of the benefits you’ll experience. I’ve lived all of these. But don’t take my word for it. Try it yourself, and see.
-
Unit tests prove that your code actually works. That means you have fewer bugs. No, unit tests can’t replace system and acceptance testing. But they do supplement it. The fewer bugs that make it to SQA, the better.
-
You get a low-level regression-test suite. You can go back at any time and see not only what broke but where the bug is. Many teams run the unit test suite as part of the nightly build. It’s a low-effort way to catch bugs before the build goes off to SQA.
-
You can improve the design without breaking it. This is actually part of step 3 above, the refactoring step. So test-first code usually doesn’t need to be refactored. I have worked on some systems that were so screwed up, like a psychotic individual, you just couldn’t untangle them. Having unit tests in place, you can do powerful refactorings that can untangle the most challenging of system psychoses.
-
It’s more fun to code with them than without. You know what your code needs to do. Then you make it do it. Even if you don’t have a working system, you can see your code actually run and actually work. You get that great “I’ve done it!” feeling. Now repeat every minute or so. Just try test-first if you want to be high on endorphins, proud about your work, and motivated to do more.
-
They demonstrate concrete progress. You don’t have to wait a month for all the pieces of the system to come together. You can show progress even without a working system. Not only can you say you’ve written the code, you can actually demonstrate success. Of course, this is another distinction that traditional programming teaches us to ignore. “Done” doesn’t mean you’ve written the code and checked it in. “Done” means the code actually runs in the system without bugs. Running unit tests is a step closer to the latter.
-
Unit tests are a form of sample code. We all encounter library functions and classes we don’t know how to use. And one of the first places we go is the sample code. Sample code is documentation. But we don’t usually have samples for internal code. So we’re left sifting through the source or through the rest of the system. Because Bob, the guy who wrote that code, is no longer with the company, so we can’t actually ask him how it’s supposed to work. But unit tests are documentation. So when you can’t remember how to use class Foo, read the unit tests to find out.
-
Test-first forces you to plan before you code. Writing the test first forces you to think through your design and what it must accomplish before you write the code. This not only keeps you focused, it makes for better designs.
-
Test-first reduces the cost of bugs. Bugs detected earlier are easier to fix. Bugs detected later are usually the result of many changes, and we don’t know which one caused the bug. So first we have to hunt for and find the bug. Then we have to refresh our memories on how the code is supposed to work, because we haven’t seen it for months. Then finally we understand enough to propose a solution. Anything that reduces the time between when we code the bug and when we detect it seems like a obvious win. We consider ourselves lucky to find out about bugs within a few days, before the code is shipped to SQA or to customers. But how about catching them within a few minutes? That’s what test-first accomplishes with the bugs it catches.
-
It’s even better than code inspections. Code inspections, they say, are better than testing, because using them to detect and fix bugs is cheaper than testing. After the code ships, it’s much more expensive to fix the bugs. The earlier we can detect and fix bugs, the easier and cheaper and better. That’s the advantage of having code reviews: Code inspections catch more bugs within a few days, rather than a few months. But test-first catches some bugs within a few minutes instead of a few days. It is even cheaper than code inspections.
-
It virtually eliminates coder’s block. Ever wonder what statement to write next? Like writer’s block, coder’s block can be a real problem. But test-first systematizes the structured part of coding, allowing you to concentrate on the creative part. You may get stuck on how to test the next bit or how to make the test pass, but you’ll never be left puzzling over where to go next. In fact, usually you’re left with the opposite problem: You know you need to take a break before you burn out, but you’re on a roll and don’t want to stop.
-
Unit tests make better designs. Testing a piece of code forces you to define what that code is responsible for. If you can do this easily, that means the code’s responsibility is well-defined and therefore that it has high cohesion. And if you can unit-test your code, that means you can bind it as easily to the test as to the rest of the system. Therefore, it has loose coupling to the pieces around it. High cohesion and loose coupling is the definition of good, maintainable design. Code that is easy to unit-test is also easy to maintain.
-
It’s faster than writing code without tests! Or to put it another way, skipping unit tests is faster, unless you actually need the code to work. Most of the effort we spend on code, we spend fixing it after we’ve checked it in to the source-code repository. But test-first eliminates much of that waste by allowing us to get more of it right to start with and by making bugs easier to fix.
Even with all this, many software developers continue to hold on to their old ways. If you are a process evangelist in your organization, you may find yourself up against some of them. I wish you the best. Just remember, people don’t buy into something just because they want it or because it sounds good. They only buy in when they’re desperate, when they need it so badly that they can taste it. I hope you can pull something from this list to help you in your endeavor.
However, if you are one of the former, one of those curmudgeon coders who would rather be right than to design good software… Well, you truly have my pity.
Why Agile Software Development Techniques Work: Improved Feedback
How TDD improves development speed and is very cost effective
To obtain good code, writing tests and code is faster then code alone
Good list! I forgot #4 when writing my own article on the advantages of TDD over unit testing after the fact, and it is an important one.
Do you mean “trips over his own feeT backpedalling”?
(OK, so they trip over their own data feed. Very funny. 😉
Hi, Jeffrey & Jeff.
Re: #4. This is actually one of the first things I noticed about test-first. (Or even test-soon-after.) Now that I’m more used to it, I don’t notice it as much. But I do notice the difference between tested and untested code. Why, just yesterday… But that’s another story.
Jeff, Yes, I meant “trips over his own feet.” Data feed: 😆 Same difference I guess.
-TimK
Some more….
N) Pins Functionality. If you don’t have a Unit Test for some functionality, don’t whine if your colleague in a later release breaks / masks / removes or otherwise destroys functionality you implemented.
M) Stops Bug Ping/Pong dead. Bug ping/pong is where the fix to a bug in interrelated functionality creates a bug and the fix to that bug recreates the first bug and the fix to that bug ….
O) Tests are code too, so tests also have bugs. By writing them test First, so they fail (when they should), you test the tests.
Hi, John. I like these, too. N is related to (but different than) my #2. M is also a good reason to run unit tests. And O is an excellent reason to test first instead of after. In fact, it’s a reason I myself frequently use to motivate myself to go through the proper red-green cycle, rather than just write the code first, since that seems more convenient. Writing the test first and seeing it fail assures me that it’s actually running and that it’s actually testing some new functionality.
-TimK
#7 is why I don’t always like to write unit tests before I start coding. My unit tests start coming part of the way into coding when I don’t really know what the final result will be until I’ve explored what needs to actually be done.
Joshua
I Pity The Fool Who Doesn’t Write Unit Tests
J. Timothy King has a nice piece on the twelve benefits of writing unit tests first. Unfortunately, he seriously undermines his message by ending it with this: However, if you are one of the [coders who won’t give up…
Hi, Joshua. If you take small enough steps, you shouldn’t experience this problem. You still do need to think about where you’re going, though.
-TimK
“Write enough code to get the test to pass, but no more.”
This is a flashing red light for me – people taking this too literally and producing something that fails as soon as it is put into the real world through not being flexible enough (eg: hard-coded paths allow the test to pass). Some authors are not communicating clearly that this means ‘don’t add unnecessary bells and whistles’ (good), rather than ‘limit the code to a literal set of cases and results’ (foolhardy).
However, that’s not a problem with the methodology, but I have in the past been badly burnt inheriting a failed project from a test-driven enthusiast who thought he was following a failsafe methodology but ended up proving that “there is no substitute for knowing what you are doing.”
Hi, Paul. I agree that you need to know what you’re doing. If a developer limits his code to a literal set of results, when he needs a more general solution, he needs improvement in two areas. First, he needs to realize that his tests may not exercise every literal case. They only exercise representative cases. Second, he needs to think about the whole range of inputs and outputs, including error conditions, and think about how the code ought to respond in each case.
That said, I haven’t yet met a developer who’s gone down this path. Usually, I see the opposite. A developer is used to putting in extra code, which may never even be executed. This is especially true for error conditions, which most developers write on the fly and many never even test. If he is to use test-first, he has to remind himself to be more disciplined in how he writes his code. He has to think about what the error cases are, write representative tests for them, and define how the code ought to behave in each one. In fact, I myself still wrestle with this, because thinking about these things is hard.
But I still think it’s better than writing code that never gets called or that doesn’t even work. And I see a lot of this. I’m currently refactoring a massive function that has numerous cases like this. Some of them return the wrong error code. Some error codes will never be generated, because the error case logically can never happen. Frequently, I’ll run across cases that will never happen but ought to happen under some conditions. It’s just that no one ever actually tested the error cases. Yet all this code is in the codebase, and we spend valuable energy maintaining it, despite the fact that it doesn’t work.
In summary, yes, there’s no substitute for knowing what you’re doing. The best we can hope for is to adopt a process that allows you to focus your skills and creativity where it will do the most good.
-TimK
[…] J. Timothy King’s Blog » Blog Archive » Twelve Benefits of Writing Unit Tests First Why do programmers hate writing unit tests? Why do they hate even more writing unit tests before coding? You don’t have to answer. I’ve already heard the excuses. These are rhetorical questions. I have a theory, however, what the real reasons are. Most software developers have never seriously tried test-first. Or if they tried it, they did not do so in a supportive environment where they could learn what they were doing. But more likely the former. So they come up with excuses: “We don’t have time to write unit tests.†Or: “Unit tests can’t bullet-proof your code.†They’re reacting out of fear, not out of empowerment, trying to find reasons for being miserable, rather than making their lives better. The neat thing is that occasionally you’ll find one of these developers who, when you face him with the facts, nearly trips over his own feed backpedalling. That’s fun to watch. And he does this all without ever taking back his opinion of test-first. That’s wicked philosophical. […]
Twelve benefits for doing TDD
[…] Twelve Benefits of Writing Unit Tests First TDD (tags: pm scrum work sdlc) […]
[…] Twelve Benefits of Writing Unit Tests First: How do you solve a software problem? How do they teach you to handle it in school? What’s the first thing you do? You think about how to solve it. You ask, “What code will I write to generate a solution?” But that’s backward. The first thing you should be doing— In fact, this is what they say in school, too, though in my experience it’s paid more lip-service than actual service— The first thing you ask is not “What code will I write?” The first thing you ask is “How will I know that I’ve solved the problem?” […]
ИнтереÑни връзки 2
C# Best Coding Practices
Naming Conventions for UI ControlsStringBuilder
vs. String / Fast String Operations…
[…] J. Timothy King list the following twelve benefits you get from doing Test-First programming: […]
[…] Twelve Benefits of Writing Unit Tests First The ones I found significant: […]
[…] Understand the material before you write. If you understand the material, you should be able to explain it to a four-year-old. If you don’t understand it, you won’t be able to explain it to anyone. You’d think this would go without saying. You’d think that programmers particularly would understand the code they write. But frequently they don’t. If they did, they would use unit tests. Instead, they tweak and hack until the code does what they think it’s supposed to. And they end up with a mess. And then they do it to their comments and other documents, too. This is what causes a lot of comments like “This member is the anticipation for audio data to synchronize with the video frames in an interleaved AVI.” The developer who wrote this probably knew how to tweak the data value to get the result he wanted. But he very possibly did not understand what that value actually represented and how it conceptually fit into the rest of the system. […]
[…] ran across this post today. It’s more than a year old, but it’s still quite […]
[…] “Twelve Benefits of Writing Unit Tests First” […]
I am new to TDD, having been driven there in desperation after my brain ran out of space to contain enough of the code in working memory to debug it. When you need a bigger brain, it’s time for unit tests.
As a newbie (but I hope a rational one) I am assessing the pros and cons of this approach.
Pros: No need for a bigger brain
I can write tests in the language of my choice (python & numpy in my case)
[insert 12 other reasons here]
Cons: I am writing mathematical functions so I still need code to generate the tests (unless I test at the level of A=B*C, which is silly), so the code is written twice
Significant refactoring of code requires significant refactoring of the tests unless I write the “God test” which is useful, but hardly TDD
[…] Using the Red-Green-Refactor mantra, we can create testable ASP.NET MVC controllers and benefit from the use of TDD. So, let’s […]
[…] to make sure your model classes are doing what you expect them to do, and there are lots of side benefits as well. The Android SDK makes it easy to create a jUnit test project. Unit tests, it’s […]
Tim, Very good Unit Test points which you have mentioned above.
[…] Monitor The “Works on My Machine†Syndrome I Pity The Fool Who Doesn’t Write Unit Tests Twelve Benefits of Writing Unit Tests First Code Tells You How, Comments Tell You Why Discipline Makes Strong Developers The Law of Leaky […]
I would agree that 4, 7 and 10 are benefits of TDD. However, the other 9 benefits on this list (and the 3 that were posted later) are just benefits of writing unit tests. In other words, they apply equally to code-first test-afterwards development, provided you are disciplined enough to write the unit tests every time.
So they’re all good points, but I feel that the title is misleading.
To me, it seems that one of the most important benefits of TDD is that you avoid the sort of scope creep that results from the developer thinking “we might need this feature later”, when there’s actually no requirement for it. Your application ends up consisting of the minimum possible amount of code that will meet the requirements, with no redundant logic or built-in bloat.
I think it needs to be made clear that TTD (Test To Destruction) has severe limitations.
1. As a designer, I cannot specify (or write) a unit test for “UI must look good and be intuitive to use”. TTD makes coders think they’ve done a great job, with little green lights to prove it, when in real (user) terms they’ve done a horrible job.
2. I cannot write a unit test for “The result of the CFD simulation must be this set of 1e6 X-Y-Z-V values” – how do I generate the test data? Back of an envelope? Excel?
3. The tests need access to the production code, and the more detailed the tests, the lower level the access – breaking encapsulation.
Not trolling, just spouting heresy.
Hi, Doug.
1. If you’re saying unit tests are not the only kind of testing you need… I agree! (To state the obvious.) But just because you need a full testing regimen is no reason to neglect or discount the value of unit tests, which are part of that regimen. And if those little green lights are making you think you’ve done a complete job, my advice would be to think again. Because they’re just the first, important part of the process.
2. Unit tests and other automated tests can actually be written for many things that programmers say they can’t write tests for, including user interfaces and simulation equations. How to do so is too involved a topic for me to cover here in this brief comment. Sometimes writing automated tests is expensive—and sometimes even more expensive than the value they impart—but whenever a programmer says “I cannot write a test for…” I know he’s being untruthful with me and with himself. Yeah, maybe you can’t program a computer to tell whether a web page looks good, but I bet you could codify the graphic designer’s layout in a unit test, at least enough to check for obvious coding errors. And I bet you could automatically test for standards-conformity, too. Or maybe it’s silly to write a unit test to run through a million simulated output values; but it’s not silly to write a unit test that spot-checks a dozen important edge-cases.
3. Unit tests are bound tightly with the units they test. That doesn’t break encapsulation. It’s what tests are for! Any time you change the code, you should have to change a test. (And you should change the test first.) Otherwise, the test is not actually detecting errors in your code.
-TimK
Responding to Doug – it seems to me that you’ve missed the point of what TDD is about. It’s really not a testing methodology; it’s a design and development methodology. The idea is that by framing your requirements in the form of test cases, you can be sure that your application code meets its requirements, and also that you don’t end up writing code that doesn’t contribute to a requirement. TDD is therefore primarily producing a design for your code; the unit tests are just an added bonus.
Your point 1 – it’s true that TDD won’t design your UI for you, or produce tests for usability or “nice looking” UIs. But there are other techniques for doing this. TDD won’t make your morning coffee either; but it’s a mistake to consider this a shortcoming.
Your point 2 – you absolutely CAN write a unit test that requires the output of your process to be a million or so well defined values. So long as you have those values in some usable form, such as a database or a CSV file, you can write a test that runs your process and checks its output. However, such a test would be brittle and unfocussed, and wouldn’t really fit into TDD.
Your point 3 – your tests need the same access to the SUT as other clients of that SUT. This doesn’t break encapsulation. In Java, for example, you’d simply put your test classes in the same package as the SUT, or as clients of the SUT; then there’s no need to provide special low level access for your test classes.
[…] Twelve Benefits of Writing Unit Tests First […]
[…] benefit to testing first, as I see it, is as it concerns the thought process towards your code. As J. Timothy King States: You ask, “What code will I write to generate a solution?†But that’s backward. The […]
[…] take to heart. If you care to read more about why testing is important there are other blog posts that will provide a much more in-depth analysis than I. However I will quote […]
hi,
Is it able to prove TDD is better then code inspection.if possible please send related paper to this id.saranarunmallur@gmail.com
I don’t believe it’s possible to prove TDD is better than code inspection, or any other technique. (Of course, I’ve been out of the loop for a while, so more interesting experiments may have been done that I’m not aware of.) My personal belief is that developers should be doing TDD, code inspection (or pair programming), and QA testing—all three—because each catches problems in a different way and at a different point in the process. The idea is to catch as many bugs as you can, as early as possible. Because earlier is cheaper, and the only bugs you aren’t interested in catching early are the bugs that you wouldn’t mind seeing delivered to customers.
-TimK
[…] articles that discuss the benefits of TDD: Twelve Benefits of Unit Tests, Research Supports The Effectiveness of TDD, TDD Research Findings, How TDD improves development […]