Who do you think you are, Jon Skeet? I know for a fact that he writes comments. I'm willing to bet he does unit testing, too.
"If builders built buildings the way software developers wrote programs, then the first woodpecker that came along would destroy civilization."
— Gerald Weinberg; Weinberg's Second Law
Along with debugging, testing is one of the most important (and often most-neglected) aspects of software development (so much so that it's core to the Agile methodology's philosophy and to test-driven development). However, most developers tend to test after they've written a whole span of code and only do integration testing (if they do any testing at all), before throwing code over the wall to QA and Tech Support. Here, then, are a number of excuses for not doing unit testing, followed by refutations for them.
Excuses for not Doing Unit Testing
"I aim to live until I die; no more and no less."
— Eddie Izzard; British actor and comedian
- We're a small team, so we know what our code does: You might know, but it's highly likely that I don't. Despite the fact that I've been working in software and Web development for over a decade, I don't know everything that I could about it. It's also the nature of my job that I regularly face new challenges for which I need to provide solutions. I can never be entirely sure what my untested code does (let alone someone else's, especially if it isn't adequately commented or modular) and I can't always ask, oftentimes because the original developer has left the company. (A large part of my job over the last five years has been reading other people's code and struggling to figure out what it does so that I can change it without breaking it.) I've also been in the situation where I've had to come back to and update code more than six months after I wrote it. I hate repeating myself (and that includes familiarising myself with code I once knew), which is why I appreciate comments and well-named tests.
- It takes too much time to write the tests: This complaint is often a mainstay of developers new to unit testing (particularly using certain frameworks, which can be difficult/frustrating to learn and use). This is largely because they view testing as something that's done at the end of development, rather than at the start of (or during) development. For example, Test-driven development (TDD) pushes it right to the front, so much so that a unit test is written before writing the code it is intended to test. (It is a spec of the API's implementation, since unit tests are only done on public functions/methods.) Then, both the system under test (SUT) and the test are modified until both are correct. If, on the other hand, you leave testing until after you've written the code, it will take you until the heat death of the Universe to write adequate test coverage (or so it seems, anyway). The longer you leave it and the more code you write, the harder it gets to test it. The same is true for debugging, a painful activity that can be significantly reduced by writing adequately comprehensive and simple tests.
- It takes too long to run the tests: It really shouldn't. A typical unit test deals with exactly one function or method. If your methods are well-composed, they will perform one specific action and the test should test that it does exactly that (no more and no less). In the case of functionality that requires/uses dependencies (including resources such as database connections and queries), you should use mocks and stubs as substitutes. Most likely, long-running tests are the domain of integration testing. Even in the cases where they aren't, one should have more than one test suite, so that it's easy to run one-off tests on a daily/weekly basis and frequent tests every time a significant change is to be committed to version control.
- My legacy code is impossible to test: Testing legacy code is certainly a difficult, painful and thankless task, but not impossible. There are ways to get a handle on it. The problem doesn't lie with unit testing, but with the poorly-written and tangled mess of code (which is often devoid of helpful comments, bien sûr). At some point, the code will require refactoring so that it is easier to reason about and understand. Doing so to aid in testing it is a worthy goal, too. Coding in a culture of fear because you are terrified of breaking legacy code is not productive. It's bad for the project, awful for the programmers, and ultimately detrimental to the business. Introducing both comments and unit testing helps alleviate the situation.
- It's not my job to test my code: What, exactly, is your job, then? Undoubtedly, your job requires you to produce maintainable, stable and working code. If you're throwing code over the wall to a QA group without doing at least some testing beforehand, then you are not doing your job. Clean up your own mess, for the sake of courtesy if nothing else. Besides, continually/repeatedly submitting buggy code is not beneficial to your career or reputation. On the other hand, if QA and other testing teams find it difficult to break/fault your code (which is their job), then your reputation as a competent developer will grow.
- I don't really know how the code is supposed to behave, so I can't test it: If you genuinely don't know what the code is supposed to do, you shouldn't be writing it. Clearly, something went wrong in the design and/or requirements phases. Besides, if you don't know what the code is supposed to do, then how will you know if it does it correctly?
- My code compiles and that's good enough: The fact that code compiles really doesn't mean much, other than that the syntax is correct. That doesn't mean anything for the correctness of the logic, of which the developer will have a far better idea than the compiler. Did you really intend to add that item to the list twice? Probably not, but the compiler won't catch that error.
- I'm being paid to write code, not to write tests: False. What you're being paid to do is produce quality code that adds value for/to the business and is fit for a particular purpose. You're certainly not being paid to spend a large amount of time single-stepping through your code, line by line, with the aid of a debugger (a painful activity that can be eliminated by writing and running good unit tests).
- I feel guilty about putting testers and QA staff out of work: Well, your feelings are misdirected. Unit testing is specifically for developers and is different from the type of testing, validation and verification that QA folks do. (It's the minimal and simple testing that should get done before handing code to them.)
- My company won't let me run unit tests on the live system: Well, of course not! By the time code makes it to the live system, it should have already been thoroughly tested. Testing should happen on a test server (even if that happens to be your own machine, while you're developing). If part of your test suite has dependencies (such as resources), you can always simulate those using mocks and stubs. At worst, replicating the live system locally (or using on-demand compute instances) isn't a huge mission and takes a few days at most, often less if done regularly.
- Yeah, we unit test already: It's possible that you or your team are conflating unit testing with other types of testing, such as acceptance or integration testing, which require more setup and are far more extensive and time-consuming. If tests aren't maintained or allowed to fail without corrective action being taken, that's not unit testing; it's time-wasting. Fix the bugs (preferably in the system under test, but also in the tests if necessary) and carry on.
- I don't know how to unit test; it's really difficult to get started: This is, possibly, the only legitimate excuse on this list. The reason for that is simply because it can be changed by learning how to do it. Admittedly, I have struggled in vain to get Xunit, FluidAssertions and Moq to work in Visual Studio, but that's likely because I don't know what I'm doing and what the right way is. Reading up on them and persevering will likely change that. Perhaps that was once true of JUnit and NetBeans, although I can only recall it just working (perhaps because I am familiar with it).
Excuses for not Writing Comments
Some of these are related to excuses for not doing unit tests (or at least have similar refutations), possibly because developers who don't do one have the same mindset when it comes to doing the other.
"A delayed game is eventually good, but a bad game is bad forever."
— Shigeru Miyamoto; Nintendo (Attributed)
- I don't have time to add comments: If you don't have time to comment, you probably don't have time to think about, design, plan and implement your code well (which is why comments will be good for later on, when you won't have time to read and struggle to comprehend your code). You should write the comments before you write the program. That's so you can collect, focus and organize your thoughts so that your program is well-designed from the start. (This is also why you should write tests before you write the code they'll test.)
- I'll come back and put in comments later: No, you won't; you'll rush on to the next bit of code. Comments should come first, while the ideas are fresh in your mind, not once you've forgot what you wrote and why. Comments added later are usually inadequate and might as well not exist.
- My code is self-documenting/explanatory: Maybe, just maybe, it is. However, that takes a considerable amount of forethought, design and planning, which is often the result of many years of experience. If, however, you've had to do maintenance on an existing code base (particularly one developed by someone else), or been subject to code reviews, you would know well that this is almost invariably not the case. As someone who has done a lot of that work, making such a statement inclines me to hate you. Many languages, such as the C-based ones, are cryptic and terse. Natural languages are far better for conveying ideas to other humans (not that developers are notably good at that, mind you).
- The documentation/specification is in my head: Good for you, mate! You do realise that it's volatile memory and not shared with the rest of the team (present and future), right? Good luck trying to impart it to them six months or more down the line, especially if you get interrupted before you've finished. Write it down; it'll be easier to share that way. Besides, programs written this way will invariably be rewritten. You might as well save yourself (and others) the trouble of wasting effort on duplication.
- Our code quality is no worse than that of anyone else in the business: Being on the same level as everyone else doesn't give you a competitive advantage. Code quality is important, even if it's not tangible/visible to the end user. Quality code is often smaller, more effective, more efficient and more extensible/maintainable than a product that has been rushed to meet a deadline. (This is also a good reason for doing unit testing.) What time you save in rushing to market will invariably be lost many times over in debugging and maintenance. Management never budgets for that.
- We don't have time for code reviews here: For comparison, see the excuse about writing/running unit tests taking too much time. There's some overlap. If you don't have time to review, you certainly don't have time to debug, which you will end up doing a lot of. It's a given that no developer writes flawless code or is able to catch all the bugs that are introduced (so much so that we should be called enbuggers). Code reviews done by fellow developers (especially more experienced ones) are one of the most valuable activities that can be performed to ensure that the code is free of most bugs, extensible, maintainable and stable. The feedback also helps developers hone their craft (if not their egos).
All of that stated, there are also excuses for writing low quality/too many comments (as can be seen in the posts linked in the "resources" section). Personally, I'll err on the side of having comments and not needing them than needing them and not having them. The latter is more frustrating than the former.
Conclusion
If you don't have time to design, plan, do the code reviews and unit tests, you shouldn't write the code for programs. If you do only half the job, you'll spend at least ten times as much on maintenance and technical support as you would if the job was done properly to begin with. You'll be perpetually stuck on "90% done", or worse. Please, please, please, for the sake of your own sanity and that of your fellow developers, write comments and do unit testing!
Thumbnail image generated by/with the Fake O'Reilly Book Cover Generator