When I think about legacy code, I think about code nobody wants to work on, code nobody understands, code the guy who’s about to retire works on. But when it comes down to it, “legacy code is simply code without tests,” as Michael Feathers, founder and director of R7K Research & Conveyance, writes in his book Working Effectively with Legacy Code.
This definition is the best I’ve seen because it provides truth to the term instead of generalizing any language decades old as legacy. Everyone beats up on COBOL programs as legacy code. The reality is there is legacy Java, legacy Ruby, legacy anything that is without tests, without a way to verify if it is working correctly.
Verification begins with unit testing, a process that allows developers to test the small parts of an application to find and fix low-level bugs before moving into testing processes that involve larger parts. By finding these bugs earlier on in the unit testing phase, developers can ensure smoother testing of applications after shipping them over to test/QA teams.
Unfortunately, most mainframe shops can only provide step-by-step documents that describe how to manually unit test programs. When you consider how many mainframe programs there are and how frequently they must change for business or regulatory reasons, manual testing programs with or without written instructions is a Herculean task.
Consequently, a lot of code on the mainframe isn’t tested as well or as often as it should be. This only increases internal company issues and external business or security compliance risks.
Three Benefits of Unit Testing
When you don’t unit test, you don’t reap benefits that impact the entire ensuing process and the quality of an application. These benefits include:
- Improving developers’ understanding of code. Creating unit tests makes your developers think in more detail about what the code should do and how to test the requirements.
- Avoiding inadvertent logic changes (bugs!). When you have many people working on code, issues occur. Unit tests flush these issues out quickly in a continuous build process. If the nightly build automatically running the unit test starts to fail, developers can investigate the cause and discern the issue quickly because the unit test failed soon after the bug was introduced. With manual testing, you often don’t discover the bug until months later when the developer may not remember writing the code or may not even work at the company any longer.
- Allowing subsequent testing phases to move faster. Functional/load testing and, most importantly, user acceptance testing all go faster and more smoothly when unit tests have flushed out the low-level code bugs. If you enter one of the aforementioned testing phases with many low-level bugs, you’ll encounter many false starts. Often, whole code areas can’t be user-acceptance tested because users can’t even access the parts of the program to review. It’s a waste of time if everyone comes in on Saturday to do user acceptance testing but can’t because some low-level issues weren’t discovered in the unit testing phase.
Companies can improve their legacy code and ensure less risk by moving away from manual processes and using automated unit testing on the mainframe to get these benefits.
Automated Unit Testing on the Mainframe
Let’s envision a world where your COBOL programs have unit tests that run automatically.
When the business calls and asks IT to make changes to a program, people won’t run and hide. They won’t dread working on it due to fear that some horrible sets of bugs will be exposed by code changes, causing them to work all weekend.
It’s no surprise individuals would have a personal aversion to code without tests at the unit level, but from a corporate perspective testing is about risk management. If you’re creating a business income report, the cost and consequence of getting it wrong can be high. The risks could include a bad business decision and being contacted by your friendly IRS auditors asking questions that must be answered.
You can focus unit tests on business rules to reduce the risk of an expensive miscalculation, and by automating them you can do this regularly and frequently. Many errors are easy to detect when using unit tests to verify boundary conditions. These boundary conditions can’t be hit by your selection of typical data taken from production and masked personal data, at least not until some unusual event like an amazing Black Friday sale or the day the stock market crashes 800 points.
If legacy code is code you can’t confidently make changes to, introducing unit tests gives you more certainty that individual, smaller parts of your code will go through changes well and stay secure through ensuing testing phases.
But manually creating and running unit tests is such a tedious, time-consuming, costly process that most shops don’t bother with it. With automated unit test creation and execution, it’s likely you can cut down hours to minutes and start making sure your code is fully tested and secure for changes.
Photo: Flickr: Matthijs