Why do we create unit tests?

A friend of mine asked me the question: "Why do we create, validate and peer-review unit test scripts?"

I was not sure how to answer this, so I took a look at some of the work/thoughts of people smarter then me (James Bach, Brian Marick, Cem Kaner, Jonathan Kohl, Bret Pettichord, Rex Black, Dave Liebreich) and I added my own thoughts and experiences. The following is what I sent her - along with some links to some good books and websites on the topic.

There are two main contexts from which I can answer this question. The first is through the context of the software development lifecycle (SDLC) and the second is from the context of the unit test itself, as an individual unit of work in the overall testing effort for the project. That is, when looking at the SDLC, unit testing can sometimes involve a methodology - as in test driven development (TDD) typically used in extreme or agile programming - or a unit test can sometimes be used as a closing artifact for a phase - as in a regulated waterfall methodology. When looking at a unit test as an individual unit or part - under the overall testing process - it can be easier to see its value outside of the implications it has on the SDLC in which it is being utilized. I will answer this question from the context of the latter, looking at a unit test as an individual work effort within the overall software testing process.

Unit tests are typically created for the following reasons:

  1. Unit tests provide quick feedback to the developer.

    The sooner a developer receives feedback on their code the sooner they can implement a change based on that feedback. The unit test provides feedback to the developer before other test systems exist for the code under development and provides feedback in the code environment closest to the developer. It is less expensive to fix errors found in a development environment then errors promoted to test environments. It is less expensive to fix errors found by the developer writing the code then when the error is found and recorded by a tester. The time to record, process, and track the error becomes overhead on the project.

  2. Unit tests provide a measure of developer progress.

    There is an increasing belief is that a properly structured system is easy to unit test. A unit test that is desirable but may be hard to implement is seen as a sign that the system needs improving. Often, developing unit tests will help focus and clarify thoughts on the code being developed by forcing issues and ambiguities to the surface before implementation and release.

  3. Unit tests mitigate concerns about the effects of refactoring.

    Unit tests are typically written at the time of the highest flux and least reliability in the code they are testing. By providing quick feedback and providing a measure of progress, developers can work confidently during times of flux and make changes with confidence, knowing that any omissions or new errors introduced should be caught by their suite of unit tests.

  4. Unit tests validate code integration.

    Unit tests can be reused during code integration to ensure that changes in other developer's code do not adversely affect the code tested via the unit test. "If it worked before it should work now." While typically not a complete test of integration, a complete test of all the parts of a whole is the logical first step in testing the whole.

  5. Unit tests result in more testable code.

    In the process of developing unit tests, developers typically a required to build in test harnesses, logging and debug utilities, and APIs to exercise functionality in isolation. Almost always, this functionality can be used downstream in the testing process during integration testing, system testing, regression testing, performance testing, and user acceptance testing.

  6. Unit tests document the code they test.

    Unit tests can be used as a form of executable documentation for the code they test. These tests are of substantial value for programmers doing maintenance: for both programmers trying to understand their own code at a later date and when looking at someone else's code. The simplicity of unit tests clarifies the intent and the expectations of the code.

  7. Unit tests are inexpensive to run and maintain.

    Relative to all the other types of testing, unit tests are inexpensive to create, maintain and run. Tools are typically free or included in the enterprise IDE being utilized, resulting in no additional tool investment. Unit tests are typically coded in the same language as the code they are testing, resulting in no additional cost associated with maintaining a specific language skill set. Unit tests are typically simple enough that no extra documentation is necessary for their longevity, unlike all other types of tests (with the exception of exploratory testing).

  8. Unit tests report serious problems.

    For a good many unit tests, failure would indicate a very serious problem if it were to occur. Unlike system tests, with can involve subjectivity and ambiguity, unit tests typically focus on technology issues and coding errors.

Aside from their immediate value to the developers, as shown above, unit tests provide value to the overall testing process:

  1. Unit tests create a test harness that can be leveraged for other types of testing.

    As described above under "Unit tests result in more testable code," unit tests and unit test harnesses can be leveraged elsewhere. Unit tests can be repurposed to address risks that may not have been envisioned when they were written. Unit test can be used as a starting point for APIs used for test automation. They can be used to seed a suite of automated tests. Logs developed for unit testing can be leveraged throughout the test lifecycle.

  2. Unit tests reduce the overall scope (coverage analysis and risk analysis) of other types of testing.

    System tests can be designed by reviewing the existing unit tests, and in some cases tapping into interfaces the developers had written for their own tests, and can focus on efforts that the developers didn't focus on. The system testers should take advantage of unit tests and design their own tests to mitigate risks not already addressed by the unit tests.

  3. Good unit tests remove the necessity of in-depth domain testing.

    With the exception of a quick sampling to verify that the right testing was done at the unit level, domain testing can be omitted entirely.

  4. Good unit tests remove the necessity of in-depth boundary value analysis.

    With the exception of a quick sampling to verify that the right testing was done at the unit level, boundary value analysis can be omitted entirely.