Big projects can have frustrating levels of complexity. Unit tests can help us cope with this frustration through two interrelated means:
- They force us to break the project down into small, testable pieces that are fun to code. In short, they help us find the simple parts of a big, complex project.
- The search for code that is easy to test can help us discover an architecture for our program that is simple and clean. Such architectures can be achieved by refactoring existing code.
In this article I will spend a few paragraphs explaining each of these ideas, and showing why they are important.
The Frustration of Complexity
Programmers get frustrated when faced with complexity. Frustrated people are not productive. If I’m frustrated by a project, I don’t want to work on that project. If I don’t work on a project, then I never get anything done.
To write a unit test, any unit test, a programmer should find a simple problem that can be easily tested. It is possible to write complex unit tests, but such tests are, by definition, poorly designed.
Even complex programs have some code in them that can be easily tested through the simple mechanism of writing a unit test. By finding this simple code, even a frustrated programmer can find a bit of work that can be done easily and quickly. Work of that kind is innately enjoyable, and innately satisfying. It is the kind of work that compels one to go on working.
After having found one simple unit test to write, a programmer can begin to see what it is their code ought to look like. Scanning through their code, they may find other simple tests that can be written.
Sometimes, however, a programmer can reach a point where it is no longer possible to find other simple tests that can be written. In such a case, the only way to move forward is to begin to refactor existing code that is overly complex or hard to test.
Reduce Complexity to Simplicity by Refactoring
It is always possible to refactor code to make it testable. Even the most recalcitrant code can be rewritten to be tested. It only takes patience, and a will to succeed.
Over and over again, we hear people come up with excuses for why they cannot test their code. The most common reason for claiming code is untestable is the inability to separate the interface of a program from the business rules that drive a program. This is a serious problem, but fortunately it is one that is usually easy to solve.
When faced with such a problem, one can always find the code that needs to be tested. In almost all cases, it will be wrapped up inside an interface event handler such as a button click method. Once you find the code, you can move it into a discreet object that can be called from either the event handler, or from a unit test.
Discovering what objects need be created to handle the code that was part of your interface is not always easy. In fact, it is in this stage of programming that a developer’s skill is revealed. Not everyone has the ability to create simple, easy to use objects that can be called from either an event handler or from a unit test.
Though the skill may be a difficult one to master, it can also be an enjoyable task to perform. Once the creative work is done, the actual work of creating a testable object can be relatively simple and enjoyable.
While engaged in creating such objects, a developer may feel that no real work is being accomplished. However, this is not true. One is taking complex, hard to understand code, and creating in its place code that is easy to use. One is taking a program that is innately frustrating to work on, and converting it into a program that is easy to understand, and hence enjoyable.
After creating one set of testable objects, it is often possible to perceive how these existing objects can be decomposed into yet simpler objects. If a particular programmer lacks skills in such an area, then often they can spend a few hours with a better programmer who can give them tips on how to proceed. In this way a well designed team can work together to use everyone’s skills to convert poorly written code into well written code.
Well written code is easy to test, and fun to program. If programming is enjoyable, then programmers will put in long fruitful hours engaged in a task that is both pleasing and productive.
This process of creating simple code described in this article is driven by the desire to write unit tests. Programmers like to write such tests because the act of creating them is both enjoyable and satisfying. In fact, most programmers enjoy their work — so long as it is not overly frustrating.
We can happily write unit tests so long as we can find code that is simple to test. When we find that there is no code left that is simple to test, then that means either that the job is done, or else that it is time to begin refactoring code so that it can be made easy to test, and therefore also easy to understand.
When we are stymied by a project that seems overly complex, overly frustrating, the way out is to begin writing unit tests. The act of writing the tests will lead us, slowly but surely, to the solution to our problems.