Test Seams and Test Injections simplify ABAP Unit Tests
I will describe my experiences with ABAP Unit Tests and Test Seams. The techniques described here can be used for new code, but they are especially valuable while working with Legacy code.
Legacy Code is defined by Michael Feathers as Code without tests. Andrea Goulet defines Legacy Code as code without communication artifacts. This is a modern interpretation of the word. So in this definition a code that is new, can be called Legacy Code even by the developers who wrote the code. Just because they did not add the tests or communication artifacts they would have needed to maintain their own code properly.
The whole work described here makes sense only if the Design Stamina Hypothesis by Martin Fowler is true and the “functionality crossed the design payoff line”.
I do not think, that a single way of building tests exists that is appropriate to improve all kinds of coding. Depending on the application to be developed, different ways to implement tests should be used and there are cases when no tests might be needed at all. It is the duty of the reader to decide whether to use or not to use the techniques described here.
I started to make Unit tests a few years ago, but I had problems to break dependencies. There are techniques to use mock instances, but these required a specific more sophisticated design, and the mock classes itself added complexity to the applications. In some cases I decided to invest this effort to have Unit tests also in cases where dependencies are an issue. But in most cases I did not write Unit tests.
So for a long time I wrote Unit tests only for methods that are complex and independent from other parts of the applications. These tests where very valuable, but most of the code I wrote remained without automatic tests. In case of existing code this was even worse, as adding Unit tests to such code would have required near to always big changes. So I hardly ever added Unit tests to existing code.
A Test Seam can be added to most statements in functions and methods of classes. Test Seams can be added also to static methods, where most older techniques to add Unit Tests could not be used.
The previous techniques to handle Unit Tests in case of dependencies where complex and required a somehow sophisticated object oriented design. Applying Test Seams is at a first glance easy. There are in my opinion no good excuses anymore to write no automatic tests at all, if Test Seams can be used. Unit Tests that are written with Tests Seams might be not as good than Unit Tests made with conventional techniques, but they are better than no Unit Tests. In other cases Test Seams appear to be the best option as they are comparable simple to use.
Left without any excuse, I write now near to always Unit Tests.
Transport variable values into a Test Injection
In the test injection it is not possible to access values of variables that are defined in the test methods. Klaus Ziegler describes in his above linked blog how to do this by using public static attributes of a local class of type FOR TESTING. See the following example of a local class:
class test_container DEFINITION FOR TESTING.
result type i.
The attribute result can be read and set in the Unit Test and in the test injection. It cannot be used in normal code.
This can for instance be used to check whether a code is executed during a Unit Test. For this an empty Test Seam statement is used. In the Unit Test such an attribute can be set inside the statement Test Injection. To check whether the code is executed, test whether the variable is set.
A typical pattern while changing Legacy code
Michael Feathers described various techniques to work with Legacy Code in his above linked book. By searching for the statement “Legacy Code” a lot of resources can be found that helps while working with such code. My standard workflow when I work with Legacy Code is currently:
The tests are Characterization tests, they are made to prevent unwanted changes in the logic. They do not check for correctness. How to check for correctness is a different topic not discussed in this blog.
See package SABP_UNIT_SAMPLE (in ABAP 7.50) or the test cases made for the SAP Inside Track Hamburg 2017 on Github. This is also a good opportunitiy to test abapGit and learn how to collaborate in openSource projects for ABAP.
There is no single pattern how to code or how to change existing code. But there are two techniques I currently use nearto always when I develop new code or change existing code: