Dear Friend,
Welcome to this issue of the Test-driven Development Edition
of The Coad Letter which is based on some (of the many) words
of wisdom from Ron Jeffries. The subject we'll be discussing is "Why
Test?". I was inspired to write this issue by reading a posting that
Ron made to the XP mailing list[2]. Ron was responding to a
question from Carl Manaster about whether test code is not needed if
you could have written code that worked fine in the first place.
-- Dave
Dave Astels
dave@adaptionsoft.com
Ron Jeffries
Object Mentor, Inc.
ronjeffries@acm.org
PS. Do you lay awake nights worrying about your software projects?
Do you have the data to show whether or not they are out of control?
With eXtreme Programming you'll always have the proof that they are
on-target, on-time, and on-budget. Learn how you can sleep soundly
again at www.adaptionsoft.com.
PPS. Get this free
report by Hurwitz Group and see how Together ControlCenter helps
you improve your application-development processes while building higher
quality applications. Find out how to control costs and make the most of
your infrastructure assets.
1 The Question: Part 1
Plenty of test-laterers or test-neverers would tell you, honestly,
that they have written some routines that worked right the first time,
without testing. How they can know they work right without testing
may be another question, but let's assume for the moment it's true.
I'll be a little bolder here and assert that I have on occasion
written routines that worked right without unit tests.
If the code works when written without tests, is writing the tests
wasted effort?
2 The Response
- Yes.
That's why XP originally had the rule to test everything that
could possibly break. Now, however, the practice of testing
in XP has evolved into Test-Driven Development. You don't worry about
what needs to be tested and what doesn't. You write the tests first
to drive what needs to be written.
So, if it can't possibly be wrong, then don't test it.
- Well, almost.
Tests not only ensure that the code you just wrote works as
required, it also protects that code against the unintentional
introduction of errors in the future. This can be the result of
making a change while implementing some other feature in such a way
that the original functionality is broken. It can also be due to
making a mistake when refactoring by hand. This has two important
purposes:
- as a regression test to make sure that everything that works now
still works in the future
- as a checkpoint during integration. Since all tests have to pass
for the production codebase, all tests have to pass after you
integrate your latest work. If they fail, you've broken something.
Therefore if your code can't possibly be wrong and will never
need to change, then don't test it.
- Well, almost.
Tests also help us design the code and its interface. How?
When we write a test first, before the code that will pass that test,
we are in the place of a client of the code. We are only concerned
with what the behavior is, not how it works. The code in question is
not written yet, so we are free to choose the interface that is most
usable. This applies to functionality (e.g. I need to be able to ask
the Sale object for its total) and naming (e.g.
total(), getTotal(),
calculateTotal(), ...). This is known as intentional
coding and we'll have an issue on that in the near future.
We invariably get simpler and more clear designs when we do TDD.
Why? When we do a design phase before coding (aka Big Design Up Front
(BDUF)) we're guessing. Some of our guesses are more educated than
others. We aren't always thinking in terms of how that interface will
be used, but rather what functionality we think it will need to have.
When we design using TDD, we are making those decisions while we write
code that will use that interface. The result is an
interface that does just enough for right now, does what is really
needed, and has names that read well in real code. We are designing
the interface in the context in which it will be used.
So if your code can't possibly be wrong will never need to
change, has a perfect design, and everyone loves the interface, then
don't test it.
- Well, almost.
Tests also explain the code. Consider that with a full set of
tests (as you will have if you use TDD...if there isn't a test for a
specific function, then that function isn't there) you have a full set
of examples of how to use the code. A well written set of tests will
show what happens with boundary conditions, how to properly use the
code, and what happens in error conditions. It is a "cookbook" that
is implicitly up to date. How many times have you been on a project
and looked something up in the official cookbook, only to find that it
is out of date compared to the code? No more.
So if your code can't possibly be wrong, has a perfectly
simple design, and everyone loves the interface, and if the code and
how to use it is perfectly clear, then don't test it.
- Well, almost.
It isn't enough that the code can't possibly be wrong, has a
perfectly simple design, and everyone loves the interface, and if the
code and how to use it is perfectly clear.
We have to know in advance that this is true.
So you're right. Strictly speaking, any code that we know with
certainty, is perfectly clear in operation and usage, that works
perfectly, that is perfectly easy to use, and that contains absolutely
zero defects, does not need testing.
Can you say that with certainty about your code? I know I can't
say it about mine. That's why I use TDD.
3 The Question: Part 2
Of course tests are there to ensure that future changes don't
break the code, but - well, YAGNI (YAGNI is an XP acronym for You
Aren't Going To Need It, and was originally applied to the practice of
designing and coding for functionality that you think that
you might need in the future. The way requirements and
technology changes, there's a fair chance you're wasting time &
effort if you do anything before it's needed). Some code never gets
refactored; some code never gets broken. I suspect there are a great
many tests out there that, after first green (this refers to "the
green bar" of jUnit: when all tasks pass the progress bar is green, if
any test(s) fail the bar turns red) has been achieved, never fail
again (and I'm thinking that first green could have been achieved
without the test).
4 The Response
Tests are in fact wasted for any code that really will never be
edited, and that really works perfectly, about which you need to learn
nothing before you write it, and that is perfectly easy to understand,
and perfectly easy to use, and has the simplest possible design.
Therefore do not test such code. We'll call this the don't test rule.
Of course, for this to work, you must do it perfectly. Therefore,
if, while using the don't test rule,
- there is EVER a bug in any of your code, or
- the code EVER has to be edited, or
- you EVER see a way to make that code better, or
- anyone EVER asks a question about how the code works, or
- anyone EVER complains about the interface to the code,
you must stop using the rule and return to using test-driven
development.
5 In Closing
I hope you've enjoyed this insight into the rationale for testing
and what can be gained from it. In the
next issue we'll have a look at the standard tool for test-driven
development in Java: the jUnit unit test framework.
Dave
Afterward
Ron coached the original XP team and has been coaching XP teams
since 1996. He has been developing software since 1961. Ron created
and oversees xprogramming.com[1] and is the co-author of Extreme
Programming Installed[3].
Thanks to Ron and Carl for their kind permission to quote them and
take editorial license with their words.
Bibliography
- 1
-
Ron Jeffries.
XProgramming.com an extreme programming resource.
www.xprogramming.com.
- 2
-
Ron Jeffries.
private communication, April 2002.
- 3
-
Ron Jeffries, Ann Anderson, , and Chet Hendrickson.
Extreme Programming Installed.
The XP Series. Addison Wesley Longman, 2001.
ISBN 0-201-70842-6.