Unit Testing Isn't About the Tests

So, here's the story... A colleague of mine commented on a
code review, I submitted. "Should we add a story to our
backlog to add some tests for this file?" This seems to be a
common thing where I work... Maybe it's not as uncommon as I
hope it is.

Now, this was a little more than a refactor, which usually
warrants a test (or at least, a change in unit tests, if they
exist). However, in this particular case, the change in
functionality was actually negative... I was removing
functionality that we no longer needed. I'm not one to write
tests to ensure things aren't there. Had there been tests to
check that they were, I might have reversed them to make them
fail before removing the functionality (then removed the
tests afterwards...). In the end, I replied that I didn't
think it would be a good use of our time... Which brings me
to my point.

Unit tests are most valuable when you're writing the code.
Adding them after the fact, has limited benefit, and is
usually an exercise in humility. I strongly believe that way
lies madness...

Clear, Concise Code

The primary benefit of TDD is this... This is pretty obvious,
but I don't think people really understand how TDD helps to
accomplish this, or to what extent. When considering the
functionality I'm planning on adding, I usually have a
thought about how I'll be implementing it. Sometimes it's a
little too complex, sometimes not quite complex enough.

When I write my test first, it forces me to think of what,
exactly, I aim to accomplish with the unit of code I'm about
to write.

Write a sentence that describes it. This is usually the
hardest part. If you can't write a concise sentence to
describe it, you're probably biting off too much.

Consider your inputs. Create some dummy data, or go get
some sample data. It doesn't have to be good... it
doesn't even really need to be representative, yet...
mostly just the write type of data.

What do you expect in return. This is usually, the
easiest... It's what's left after your input has been
processed. Where do you want to end up?

After thinking through all of this, I usually have a really
clear idea of what I need to do... and while that's huge,
it's not what this benefit is about.

After writing your test... you write just enough code to make
it pass. Then, when it passes, you refactor. Refactor your
code, try to remove redundancy. Add variables to make it
clearer what is being done. Rename old variables if there is
a better name.

Be relentless! Don't allow a single line of
code that doesn't serve a purpose... Don't allow two lines
for what can be done as clearly with one.

When your done refactoring your code, refactor your test... If
your inputs aren't very representative, replace them. If you
need a larger sample of inputs, add an array and a loop... Be
sure to run your test with every little change, so you know
everything is still going to plan. If you have repetition
across a set of tests, extract that repetition out into a
beforeEach (or setup or whatever your testing suite uses).

After you've written a sizable chunk of functionality this
way, you'll look at your source and think... that's all...
It's so small... And I've been at it for hours... But, it's
beautiful! It's clear, it's meaningful, and it does exactly
what you expect it to do... and nothing else. That
is the biggest thing. There's no extra code to get in the way
when you need to make a change later. Everything there is
there for a purpose.

The Other Benefits

Yea, you get unit tests which can run on every build and tell
you if somebody changed something in a way that the test can
detect problems. That has some value, however, I've found
most bugs don't actually fail the tests... They're usually
adding functionality that you didn't test for.

You get a specification list, which describes what you're
trying to accomplish with your code... meh. If you were
ruthless enough while writing the code, it's already pretty
clear without the specs.

While these could be beneficial, especially for code that
wasn't written in TDD fashion, I don't really feel like
they're enough to go through the hassle of writing the tests.
Sure, if you're dealing with a really complex bit of code, it
would probably be beneficial to write some pinning tests to
try to nail down the current functionality, before touching
it. However, chances are pretty good that you'll miss things
(probably the same things you'd miss while just refactoring
the code in the first place).

In the end, I feel that if you're not going to practice TDD,
you may as well, not write the tests, at all... You're
missing out on the greatest benefit of TDD, by putting off
writing the tests to the end. On that note, this benefit is
huge!!! Try it for a couple of weeks... at least on
new development. If you're doing it right and you have a good
workflow, you'll never go back to writing huge chunks of code
at a time.