I've discussed why I consider unit testing to be a mandatory component of development efforts. In my travels I've encountered a number of arguments against unit testing. I've considered these arguments and they're all fatally flawed. Each argument is at its core prompted by one or more of three underlying problems:

  • Ignorance
  • Short Sightedness
  • Laziness

Ignorance in itself is not a crime. However a significant and essential component of being a professional software developer is identifying those areas in which you need to increase your knowledge or skills. My sympathy for ignorance therefore only runs to those who are willing to learn and improve.

Short sightedness is an unfortunately common human problem. Again it is my opinion that being a professional software developer involves considering more than just short term factors. Constant short term thinking leads to accumulation of enormous technical debt and greatly increases the cost of development over the project lifetime.

Laziness has been suggested as a virtue in software development. This virtuous laziness involves doing some work now to automate work to be done later, resulting in overall less work. This is not the kind of laziness to which I am referring. I'm referring to the traditional procrastination based laziness that ultimately results in more work overall. This is fine if it's related to doing your housework and no one but you is affected. It's highly unprofessional when you're doing it for a client and thus your laziness results in increased costs for them over the lifetime of the project.

The simplest argument I encounter is from people who claim that they don't unit test because they don't know how. This is a direct argument of ignorance. The attempt to make this acceptable implies that it's acceptable to be lazy in not learning how to write unit tests. I reject this assertion. Code that is not unit tested will on average be more costly to maintain, have more defects and will have inferior design. Allowing this because you choose not to take the effort to learn a technique fundamental to your profession is unacceptable.

A related argument is that it is somehow "too hard" to do unit testing. It must be acknowledged that there is a learning curve to doing unit testing effectively. I make no claim that I have mastered unit testing to the point where I have nothing left to learn. That some effort is required is undeniable. But I will not accept that it is "too hard". Anyone who is capable of writing software to the standard required to do it for a living is capable of learning how to write unit tests. If you honestly find it too hard to write unit tests then you are in the wrong field. Otherwise your protestations about unit testing are laziness.

It's very common to see arguments made that unit tests should not be written in cases of particular time pressure. This comes from the (incorrect) view that unit testing is a luxury that is to be dropped in order to focus on meeting short term goals. This argument is a form of short sightedness. Over anything but the smallest timeframes unit testing saves effort. Certainly unit testing is additional code to be written but this additional code provides a reduction in development effort that greatly exceeds the effort required to write it. This comes from such factors as reduced defects, but also from less obvious sources such as improved design resulting in reduced duplication and from increased maintainability making it easier to add or modify functionality.

It must also be considered that adding unit tests after the fact takes significantly more effort than writing them in conjunction with the code. This is compounded by tests that tend to be of lower quality, validating the current behaviour instead of specifying the desired behaviour, and the loss of design benefits gained from developing code to be testable.

Another popular argument from ignorance is that an individual can get more done when not writing unit tests. This claim of increased productivity fails for a few reasons. Firstly it fails to account for the risks of introducing defects into existing code. Adding 3 new features at the cost of breaking 4 others is hardly a net gain. Without automated testing you are relying solely on manual testing to discover the issues before you release, and human tests are valuable but fallible things.

In a team environment unit testing is a way of ensuring that the assumptions each team member is working with as to the operation of the code are captured in a fashion that provides significant confidence that violations will be detected. For instance if my code depends on a particular behaviour of a dependency and that behaviour is changed by another developer it is significantly easier to detect and correct this situation if there is a unit test that checks for the behaviour and will fail when it is modified.

Having argued that unit testing is essential for professional development and that arguments to not unit test are flawed, I shall now list cases where unit testing is not applicable. What is important to note here is that I'm not suggesting that these scenarios imply that unit testing should not be performed for the majority a system. Rather there is a valid argument that for specific scenarios within larger systems the cost of unit testing can outweigh the benefits. This is legitimate but does not imply that the majority of the system should be untested.

  • UI User interfaces are notoriously difficult to test. Larger organisations may use automated tools to validate the interfaces but at the smaller end the cost is generally far too high. Proper use of patterns such as MVC should ensure that the untested component of the code is extremely minimal and limited to presentation elements only. Separation of concerns would then ensure that the business logic and other system elements are properly tested.
  • Integration Points Some integration points simply don't lend themselves to proper unit testing. This is often because the API you are dealing with does not provide the ability to mock or stub it. As an example I once wrote an application that dealt with receiving requests via WCF then making raw TCP/IP calls to a legacy system. I was able to unit test every element of the system except for the code that dealt directly with the TCP/IP connections. This was due to a limitation in the .NET networking classes that prevented me from dealing with anything but concrete instances. Note that this point is different from integration testing, which deals with testing that system components interact correctly and as such is distinct from unit testing. In this point I am discussing unit testing of the code for integrating with other components.

This discussion applies to the particular class of systems that I work with, primarily line of business systems. It may not apply to the systems you work with. Some environments will not support unit testing and arguing that it is ignorant, short sighted or lazy to not unit test in such an environment is not applicable. This is unfortunately all too common with some customisable business applications which provide extensibility mechanisms that are so tightly coupled to the application that unit testing cannot be performed. I would argue that this would count as a reason to chose alternate applications, but this is a separate discussion.

In summary:

  • Arguments to not unit test systems as a whole are generally ignorant, short sighted or lazy
  • Professionalism in software development demands that developers learn to unit test and apply it to their work
  • Some specific tightly defined areas of a system may not be unit testable in a cost effective manner but the system should be constructed so as to minimise these elements