Following some comments on the blog Great code is testable code there seems to be a lack of good “real life” examples about good (unit-) testable code in combination with legacy or standard SAP code.
So – here is my productive example on this:
The use case is an approval process for quotations. Requirement is to check the margin of a quotation and compare it with a customizable margin value, both total value and percentage value.
The check is based on item level (percentage check) and in accumulation (total value check) for the header.
If it fails a “blocking” order reason is set and a message is added to the application log (SD_MESSAGE_COLLECT).
All the standard SD enhancing business logic is encapsulated into a singleton class ZCL_SD_ORDER_APPROVAL.
This class is integrated into the (legacy) SD user exits (include MV45AFZB).
Here I have an high-level diagram on the main steps and the integration points:
How to test this business logic via unit tests?
Test data strategies:
Have a copy of your productive environment and benefit from this test data
Make use of Test Data Container (transaction SECATT)
Make use of some hard coded test data (“fake it till you make it”)
What to test? CUT = Class under test
I suggest testing the public API (mostly the public methods) which I describe here.
How to test?
In this blog I will focus on hard coded test data
In the setup I will get my singleton instance CUT for the following test execution. Then I will add a fixed customizing value (total value) which I use in my tests per parameter injection and call the init() method to provide the actual data used in this context. In production the initialization is integrated into the userexit_read_document (MV45AFZZ).
The teardown will
clean up my test data (clear mt…)
reset some standard function module data (SD_INIT_MESSAGES) as I am adding messages to it via function module SD_MESSAGE_COLLECT
clean up my CUT zcl_sd_order_approval=>free( )
So setup and teardown will “cover” the same functionality as it is required during the order processing
Test cases (excerpt): As stated earlier is use for this development hard coded test data for the sake of simplicity.
I have some test cases which check the total value of an order against an given (customizing) threshold. So in setup I mocked my customizing with a value of 100 000. Now I have a test case with a comparing value (netwr) above and one below:
Then I have some test which checks the margin on a given item.
First I will mock per test case the minimum margin (here 5 %).
In addition i have some helper methods which i use in several testcases to add some item data with mock costs and salesprice. Here first item with 100% margin which is ok. Second item with boundary value below the required margin and third item with boundary value above the required margin:
Verify your tests:
Don’t forget to test the integration itself, as only the units are tested and not the integration within the user exits.
You can do this via normal manual tests
or create an integrational unit test which calls for example standard BAPI for sales order creation/change
Check your unit test coverage for possible untested weak points in your code
What I have not shown here is usage of a test data container. A very powerful tool especially as you can export testdata (for example via debugger) from production into development system.I try to create a follow up blog on this.