Monday 12 June 2017

Test Seams and Test Injections simplify ABAP Unit Tests

Introduction


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.
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.

Test Seams


This changed when I learned of the statement ABAP Test Seams that is available with ABAP 7.50.

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.
  PUBLIC SECTION.
    CLASS-DATA:
      result type i.
ENDCLASS.

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:
  • Add a Unit Test to understand the code and to have a safety belt
  • Refactor to simplify changes
  • Change Unit test so that it passes only after changes are made
  • Make changes in the code
The tests are Characterization tests, they are made to prevent unwanted changes in the logic. They do not check for correctness.

No comments:

Post a Comment