The tool is created to simplify data preparation/loading for SAP ABAP unit tests. In one of our projects we had to prepare much tables data for unit tests. For example, a set of content from BKPF, BSEG, BSET tables (FI document). The output to be validated is also often a table or a complex structure.
Data loader
Hard-coding all of that data was not an option – too much to code, difficult to maintain and terrible code readability. So we decided to write a tool which would get the data from TAB delimited .txt files, which, in turn, would be prepared in Excel in a convenient way. Certain objectives were set:
...
call method o_ml->load_data " Load test data (structure) from mockup
exporting i_obj = 'TEST1/bkpf'
importing e_container = ls_bkpf.
call method o_ml->load_data " Load test data (table) from mockup
exporting i_obj = 'TEST1/bseg'
i_strict = abap_false
importing e_container = lt_bseg.
...
call method o_test_object->some_processing " Call to the code being tested
exporting i_bkpf = ls_bkpf
it_bseg = lt_bseg
importing e_result = l_result.
assert_equals(...).
...
The first part of the code takes TAB delimited text file bseg.txt in TEST1 directory of ZIP file uploaded as a binary object via SMW0 transaction…
BUKRS BELNR GJAHR BUZEI BSCHL KOART …
1000 10 2015 1 40 S …
1000 10 2015 2 50 S …
… and puts it (with proper ALPHA exits and etc) to an internal table with BSEG line type.
Store/Retrieve
Later another objective was identified: some code is quite difficult to test when it has a select in the middle. Of course, good code design would assume isolation of DB operations from business logic code, but it is not always possible. So we needed to create a way to substitute selects in code to a simple call, which would take the prepared test data instead if test environment was identified. We came up with the solution we called Store. (BTW might nicely co-work with newly announced TEST-SEAM feature).
Test class would prepare/load some data and then “store” it:
...
call method o_ml->store " Store some data with 'BKPF' label
exporting i_name = 'BKPF'
i_data = ls_bkpf. " One line structure
...
… And then “real” code is able to extract it instead of selecting from DB:
...
if some_test_env_indicator = abap_false. " Production environment
" Do DB selects here
else. " Test environment
call method zcl_mockup_loader=>retrieve
exporting i_name = 'BKPF'
importing e_data = me->fi_doc_header
exceptions others = 4.
endif.
if sy-subrc is not initial.
" Data not selected -> do error handling
endif.
...
In case of multiple test cases it can also be convenient to load a number of table records and then filter it based on some key field, available in the working code. This option is also possible:
Test class:
...call method o_ml->store " Store some data with 'BKPF' label
exporting i_name = 'BKPF'
i_tabkey = 'BELNR' " Key field for the stored table
i_data = lt_bkpf. " Table with MANY different documents
...
“Real” code:
...
if some_test_env_indicator = abap_false. " Production environment
" Do DB selects here
else. " Test environment
call method zcl_mockup_loader=>retrieve
exporting i_name = 'BKPF'
i_sift = l_document_number " Filter key from real local variable
importing e_data = me->fi_doc_header " Still a flat structure here
exceptions others = 4.
endif.
if sy-subrc is not initial.
" Data not selected -> error handling
endif.
...
As the final result we can perform completely dynamic unit tests in our projects, covering most of code, including DB select related code without actually accessing the database. Of course, it is not only the mockup loader which ensures that. This requires accurate design of the project code, separating DB selection and processing code. But the mockup loader and “store” functionality makes it more convenient.
Data loader
Hard-coding all of that data was not an option – too much to code, difficult to maintain and terrible code readability. So we decided to write a tool which would get the data from TAB delimited .txt files, which, in turn, would be prepared in Excel in a convenient way. Certain objectives were set:
- all the test data should be combined together in one file (zip)
- … and uploaded to SAP – test data should be a part of the dev package (W3MI binary object would fit)
- loading routine should identify the file structure (fields) automatically and verify its compatibility with a target container (structure or table)
- it should also be able to safely skip fields, missing in .txt file, if required (non strict mode) e.g. when processing structures (like FI document) with too many fields, most of which are irrelevant to a specific test.
...
call method o_ml->load_data " Load test data (structure) from mockup
exporting i_obj = 'TEST1/bkpf'
importing e_container = ls_bkpf.
call method o_ml->load_data " Load test data (table) from mockup
exporting i_obj = 'TEST1/bseg'
i_strict = abap_false
importing e_container = lt_bseg.
...
call method o_test_object->some_processing " Call to the code being tested
exporting i_bkpf = ls_bkpf
it_bseg = lt_bseg
importing e_result = l_result.
assert_equals(...).
...
The first part of the code takes TAB delimited text file bseg.txt in TEST1 directory of ZIP file uploaded as a binary object via SMW0 transaction…
BUKRS BELNR GJAHR BUZEI BSCHL KOART …
1000 10 2015 1 40 S …
1000 10 2015 2 50 S …
… and puts it (with proper ALPHA exits and etc) to an internal table with BSEG line type.
Store/Retrieve
Later another objective was identified: some code is quite difficult to test when it has a select in the middle. Of course, good code design would assume isolation of DB operations from business logic code, but it is not always possible. So we needed to create a way to substitute selects in code to a simple call, which would take the prepared test data instead if test environment was identified. We came up with the solution we called Store. (BTW might nicely co-work with newly announced TEST-SEAM feature).
Test class would prepare/load some data and then “store” it:
...
call method o_ml->store " Store some data with 'BKPF' label
exporting i_name = 'BKPF'
i_data = ls_bkpf. " One line structure
...
… And then “real” code is able to extract it instead of selecting from DB:
...
if some_test_env_indicator = abap_false. " Production environment
" Do DB selects here
else. " Test environment
call method zcl_mockup_loader=>retrieve
exporting i_name = 'BKPF'
importing e_data = me->fi_doc_header
exceptions others = 4.
endif.
if sy-subrc is not initial.
" Data not selected -> do error handling
endif.
...
In case of multiple test cases it can also be convenient to load a number of table records and then filter it based on some key field, available in the working code. This option is also possible:
Test class:
...call method o_ml->store " Store some data with 'BKPF' label
exporting i_name = 'BKPF'
i_tabkey = 'BELNR' " Key field for the stored table
i_data = lt_bkpf. " Table with MANY different documents
...
“Real” code:
...
if some_test_env_indicator = abap_false. " Production environment
" Do DB selects here
else. " Test environment
call method zcl_mockup_loader=>retrieve
exporting i_name = 'BKPF'
i_sift = l_document_number " Filter key from real local variable
importing e_data = me->fi_doc_header " Still a flat structure here
exceptions others = 4.
endif.
if sy-subrc is not initial.
" Data not selected -> error handling
endif.
...
As the final result we can perform completely dynamic unit tests in our projects, covering most of code, including DB select related code without actually accessing the database. Of course, it is not only the mockup loader which ensures that. This requires accurate design of the project code, separating DB selection and processing code. But the mockup loader and “store” functionality makes it more convenient.
Contributors
The tools is the result of work of my team including:
- me
- Svitlana Shlapak
- Bohdan Petrushchak (aka The Wirtschaftsmann)
The code is freely available at our project page on github – sbcgua/mockup_loader · GitHub
No comments:
Post a Comment