I want to add a new side (actually, a rather old one) to this: unit tests aren't that useful if your code is well-designed.
I know most of the programmers don't do program design anymore, I still do, and I found unit tests to be a rather time-consuming necessity to fit into a TDD culture, but so far, I have met only 1-2 minor bugs per thousands of lines of tests of my code - not just what I wrote, but what official testers wrote as well.
I guess you won't do program design anymore either - the current folks will pressure you into a mold not to do it - but perhaps it's worth to remember that unit test is a very-very low efficiency method compared to design.
This is a dijsktraian argument: unit test can only be executed against a program which is detailed enough to run.
If you draw flowcharts / state/action diagrams, sequence diagrams, object inventories (and last AND least, class diagrams) for your code before actually writing it, you'll be able to kill off most of the potentially threatening bugs just by tracing around your lines and checking your names.
Nowadays class diagrams are generated from code, containing hundreds, sometimes thousands of classes and are completely unusable - anything containing more than 15 elements is beyond human recognition.
You have to know what 10+-5 classes matter or what you do right now, and be able to check your code from multiple viewpoints, each diagram representing EXACTLY the viewpoints you're looking at, and you'll kill off thousands of bugs on paper.
- Checking in a dynamic code if types are met (with just simply showing input/output types on a flowchart and connecting them with a pencil or different color),
- checking if all states are handled (by checking the fullness of your conditions),
- tracing the lines to make sure everything gets an end (every flowchart should be a fully-defined deterministic finite state automat, for the mathematically minded
- ensuring that certain components have nothing to do with each other (by extracting all their dependencies) (good for security)
- simplifying code by converting dependencies into associations (dependencies come from what classes do the member methods use)
- checking names, looking for common prefixes, erasing synonyms etc...
there's just so much easier...
Also, I found out that my applications are more useable, if they come directly from use cases... if the use cases are written well (ACTOR verb subject, Maintainer requests to open cash machine)... That
I've did code with 95+% of code coverages. Of course, I do unit tests sometimes, esp. for boundary checks in calculations, but I'm yet to meet serious regressions (serious: doesn't get wiped out in 24 hours) for not using unit tests even for refactorings.
Sometimes I don't do a single line of code for 3-4 days straight, just drawing. Then, in one day, I type in 1500-2000 lines. By the next day, they're mostly production ready. Sometimes unit tests are written (with 80+% of coverage), sometimes (in addition) testers are asked to try to break it, every single time, some people are asked to review it by looking at it.
I'm yet to see those unit tests finding anything.
I wish design-thinking would come in place of TDD... but TDD is so much easier, it's like going with a sledgehammer... designing needs thinking, and you're away for the keyboard most of the time.