I recently read an interesting blog post about why it’s ok not to write unit tests.
However, I believe the topic does deserve a proper rebuttal of some form, and I would like to try my hand.
What are Unit Tests for?
I would like to start by clearing the clutter. Much of cashto’s blog post discusses jobs that unit tests just aren’t really well-suited for.
Do unit tests catch bugs?
Not very well, no. Unit tests can catch some very small insignificant bugs, but I’m sure most people reading this will agree that the real bugs come up from interactions between code. This just isn’t feasible to test in a unit test – we need functional tests, integration tests, and acceptance tests to do that.
Do unit tests improve design?
No, but I’ll admit they make poor design painful.
I recently worked with an object that evolved to require 10 constructor parameters. Mocking 9 parameters just so you can test interactions with the 1 you do care about is painful. The poor design hurt, and we refactored to a simpler design.
Do unit tests help you to refactor?
Why failing tests create safety.
Until recently I didn’t fully get the meaning of this idea.
The big problem I saw was that failing tests just got altered or deleted. How on earth does deleting or changing a failing test make the refactor easy? Couldn’t you have just refactored without the test?
The answer hit me like an untested jar-file.
When I change a piece of code and 3 tests fail, I can read those tests and know exactly what contracts I had violated. That doesn’t mean my change was wrong, but it does make it very clear what assumptions had previously been made about the system.
This empowers me to go into any areas that used those components, and modify them based on the new contract I am trying to put in place. I now know exactly how my alteration affects other areas of the system.
What about bugs and junk?
Yes, Unit Tests can help uncover bugs, but usually these bugs are very low-level simple algorithm bugs.
When Unit Tests drive my code, the unit tests act as living documentation for the assumptions I actually care about, but they do absolutely no work to ensure other areas of the system use the component properly.
So how do you really catch bugs? By testing the system in its assembled state. This is accomplished using Functional and Acceptance Tests. Then you test the interactions your system may have with other systems using Integration Tests. These catch real bugs, not unit tests.
So that’s why it’s the corporate standard?
As cashto makes very clear, many people are well aware of what unit tests are, but don’t fully understand what they provide or what a test failure actually means.
It’s entirely possible that someone at your organization knows exactly what Unit Testing is meant to do. If that’s the case, great! Encourage them to share that knowledge with the rest of the team(s), because it’s very valuable.
Then again, it’s entirely possible that nobody knows why Unit Testing is actually done. Maybe someone just heard it was a good thing. If that’s the case, I encourage you to go out, learn about the reasons behind TDD and Unit Testing, and help educate your team and corporation.
I believe one of the primary problems with testing, TDD, and Unit Tests is a lack of understanding.
I walked out of University appropriately dogmatic about the usefulness of Unit Tests and testing. I criticized how many tests my team had (on their already slow 45-minute build cycle). I ran around adding tests whenever I could. Up until recently I didn’t know why, and I honestly believe the quality of my tests suffered for it.
Like all practices, we have to know why what we are doing is good before we can reap the full benefits.