Enhance SAP objects with great and testable code
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.
Happy unit testing 🙂