Nowadays,
when I talk with (read: rant at) anyone about why they should do test
driven development or write unit tests, my spiel has gotten extremely
similar and redundant to the point that I don't have to think about it
anymore. But even when I do pairing with skeptics, even as I cajole and
coax testable code or some specific refactorings out of them, I wonder,
why is it that I have to convince you of the worth of testing ?
Shouldn't it be obvious ?
And sadly, it isn't. Not to many
people. To many people, I come advocating the rise of the devil itself.
To others, it is this redundant, totally useless thing that is covered
by the manual testers anyway. The general opinion seems to be, "I'm a
software engineer. It is my job to write software. Nowhere in the job
description does it say that I have to write these unit tests." Well, to
be fair, I haven't heard that too many times, but they might as well be
thinking it, given their investment in writing unit tests. And last
time I checked, an engineer's role is to deliver a working software. How
do you even prove that your software works without having some unit
tests to back you up ? Do you pull it up and go through it step by step,
and start cursing when it breaks ? Because without unit tests, the odds
are that it will.
But writing unit tests as you develop isn't
just to prove that your code works (though that is a great portion of
it). There are so many more benefits to writing unit tests. Lets talk in
depth about a few of these below.
Instantaneous Gratification
The
biggest and most obvious reason for writing unit tests (either as you
go along, or before you even write code) is instantaneous gratification.
When I write code (write, not spike. That is a whole different ball
game that I won't get into now), I love to know that it works and does
what it should do. If you are writing a smaller component of a bigger
app (especially one that isn't complete yet), how are you even supposed
to know if what you just painstakingly wrote even works or not ? Even
the best engineers make mistakes.
Whereas with unit tests, I can
write my code. Then just hit my shortcut keys to run my tests, and
voila, within a second or two, I have the results, telling me that
everything passed (in the ideal case) or what failed and at which line,
so I know exactly what I need to work on. It just gives you a safety net
to fall back on, so you don't have to remember all the ways it is
supposed to work in. Something tells you if it is or not.
Also,
doing Test Driven Development when developing is one of the best ways to
keep track of what you are working on. I have times when I am churning
out code and tests, one after the other, before I need to take a break.
The concept of TDD is that I write a failing test, and then I write just
enough code to pass that test. So when I take a break, I make it a
point to leave at a failing test, so that when I come back, I can jump
right back into writing the code to get it to pass. I don't have to
spend 15 - 20 minutes reading through the code to figure out where I
left off. My asserts usually tell me exactly what I need to do.
Imposing Modularity / Reusability
The
very first rule of reusable code is that you have to be able to
instantiate an instance of the class before you can use it. And guess
what ? With unit tests, you almost always have to instantiate an
instance of the class under test. Therefore, writing a unit test is
always a first great step in making code reusable. And the minute you
start writing unit tests, most likely, you will start running into the
common pain points of not having injectable dependencies (Unless of
course, you are one of the converts, in which case, good for you!).
Which
brings me to the next point. Once you start having to jump through
fiery hoops to set up your class just right to test it, you will start
to realize when a class is getting bloated, or when a certain component
belongs in its own class. For instance, why test the House when what you
really want to test is the Kitchen it contains. So if the Kitchen class
was initially part of the House, when you start writing unit tests, it
becomes obvious enough that it belongs separately. Before long, you have
modular classes which are small and self contained and can be tested
independently without effort. And it definitely helps keep the code base
cleaner and more comprehensible.
Refactoring Safety Net
Any
project, no matter what you do, usually ends up at a juncture where the
requirements change on you. And you are left with the option of
refactoring your codebase to add / change it, or rewrite from scratch.
One, never rewrite from scratch, always refactor. Its always faster when
you refactor, no matter what you may think. Two, what do you do when
you have to refactor and you don't have unit tests ? How do you know you
haven't horribly broken something in that refactor ? Granted, IDE's
such as Eclipse and IntelliJ have made refactoring much more convenient,
but adding new functionality or editing existing features is never
simple.
More often than not, we end up changing some undocumented
way the existing code behaved, and blow up 10 different things (it
takes skill to blow up more, believe me, I have tried). And its often
something as simple as changing the way a variable is set or unset. In
those cases, having unittests (remember those things you were supposed
to have written?) to confirm that your refactoring broke nothing is
godsend. I can't tell you the amount of times I have had to refactor a
legacy code base without this safety net. The only way to ensure I did
it correct was to write these large integration tests (because again, no
unit tests usually tends to increase the coupling and reduce
modularity, even in the most well designed code bases) which verified
things at a higher level and pray fervently that I broke nothing. Then I
would spend a few minutes bringing up the app everytime, and clicking
on random things to make sure nothing blew up. A complete waste of my
time when I could have known the same thing by just running my unit
tests.
Documentation
Finally, one of my favorite
advantages to doing TDD or writing unit tests as I code. I have a short
memory for code I have written. I could look back at the code I wrote
two days ago, and have no clue what I was thinking. In those cases, all I
have to do is go look at the test for a particular method, and that
almost always will tell me what that method takes in as parameters, and
what all it should be doing. A well constructed set of tests tell you
about valid and invalid inputs, state that it should modify and output
that it may return.
Now this is useful for people like me with
short memory spans. But it is also useful, say, when you have a new
person joining the team. We had this cushion the last time someone
joined our team for a short period of time, and when we asked him to add
a particular check to a method, we just pointed him to the tests for
that method, which basically told him what the method does. He was able
to understand the requirements, and go ahead and add the check with
minimal hand holding. And the tests give a safety net so he doesn't break
anything else while he was at it.
Also useful is the fact that
later, when someone comes marching through your door, demanding you fix
this bug, you can always make sure whether it was a a bug (in which
case, you are obviously missing a test case) or if it was a feature that
they have now changed the requirements on (in which case you already
have a test which proves it was your intent to do it, and thus not a
bug).
Source:Google testing