In the next few installments of this blog I will be inviting a good friend of mine William Craig to teach us about ABAP Unit. Like myself, bill has been around SAP quite awhile and will have an interesting perspective for all you naysayers out there around the effort involved to implement ABAP Unit to existing objects vs using it out of the gate on new development. Here is a little bit about Bill….
Bill Craig is a senior developer at IT Partners. Bill is also the president of ASAP Consulting Inc., located in Warrenton, Virginia. Bill has over 10 years of experience with SAP ECC systems and integration with SRM. His areas of expertise include SAP Business Workflow and ABAP development. His concentration is on SD, MM, and Procure-to-Pay modules. You can reach him at [email protected]
As developers, we have all run into a situation where the code we are writing or changing is the easy part, but the unit test is anything but easy. Recently I made a change to a program which was straight forward, but to test I had to create a sales order, then a credit memo, then a billing document, rinse, repeat. I understand that all these scenarios should be tested, but in development it can be challenging with missing our corrupt data. With ABAP Unit test you can test all your code without having to depend upon the data.
One of the many goals of ABAP Unit is to separate the code from the database, or external calls. Writing your code so that you can use ABAP unit techniques allows you to test all the possible scenarios and not worry or be dependent upon the data in the system you are developing in. But in order to do this, it really does take a different approach to development. You need to plan out your logic, separate selects and function calls, putting them in their own class. Our goal is not to test the data in the system but rather the code you are writing and how it handles different values based upon your functional requirements.
For this example we will use transparent table SFLIGHT as our database table. Let’s say the requirement is to populate a value based on the type of plane selected from SFLIGHT. First, I create a class that is going to contain all of my selects or external calls. The trick is to not make it final so that we can redefine the method for our ABAP unit test.
Simple class to do all my external data retrieval or function calls.
In the definition of the above data retrieval class, I need to define the main logic class as a friend.
In the main class that calls ZCL_DEMO_DATA_HELPER, I need to create a constructor method that has an optional parameter as a reference to ZCL_DEMO_DATA_HELPER.
This optional parameter will only be populated when you are running your ABAP unit test. So, when it is running as an application it will create the object as a reference to your data class you are calling.
Now, for ABAP Unit test, before I even start the logic for my requirement, I go to the test class tab in Eclipse and create a local helper class inheriting from ZCL_DEMO_DATA_HELPER that I will use to redefine the methods that are performing the select statements.
As you can see, I have one new method ‘set_plane_type’, and then a redefinition of the ‘select_flight_info’ method. I will use the ‘set_plane_type’ method in my unit test to set the value that I want to have returned when I call the ‘select_flight_info’ method. This way I can unit test my main method to handle the plane type values without having to rely on the actual select statement and data in the table. The method that I want to unit test sets a variable based upon the plane type from value in SFLIGHT. Rather than trying to find the data in my development system to test how my code handles each of the possible outcomes, I can use ABAP unit test to validate my logic.
The method is simple, but will demonstrate how to use the local redefined helper class to set the value returned by our select statement.
Create the ABAP Unit test class setup either in Eclipse or using the abap unit test class wizard in SAP GUI transaction SE24/SE80.
In the test method, I create an instance of the local helper class, and call the method ‘set_plane_plane’, so that I am telling the redefined method selecting the data, what value I want returned while I am executing the ABAP Unit test.
I then instantiate object f_cut as my reference to the object I am testing. I am passing in the local helper class which is used as the optional parameter in the constructor during instantiation. Then I call the method ‘set_value_based_on_planetype‘, and using the CL_ABAP_UNIT_ASSERT class I validate the result of my method call.
When I execute the unit test with coverage you can see the parts of the method that were executed. In debug mode you can see that the class that is being called is my local definition of the helper class.
And rather than go to the select statement, it goes into the redefined method and sets the return value to the value I chose.
As I have started using ABAP Unit to validate my code, making sure I am getting a high percentage of code coverage, and detaching the data from my tests, I find that the initial design and thought process takes more time than I used to devote to this stage. It really takes some effort to plan out how you will unit test before you start developing a new object or even modifying an existing object. But what I have discovered is that once I plan it out, my development goes quicker, and it is much easier to correct as I unit test my code. ABAP Unit does not replace good system or integration testing, but it can prepare your code so that these tests can be successfully completed in a shorter time period.