Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
emanuel_klenner
Active Participant
Creating test data for a meaningful ABAP unit test can be challenging. The data preview in the ABAP Development Tools (ADT) for Eclipse can help with that for database tables or views.

Let's take table T100 (ABAP messages) as an example. First open the table definition and then click on the F8 button to trigger the data preview.


ABAP Data Explorer in Eclipse


When you right click within the Data Preview area, you get the context menu option:
'Copy all rows as ABAP value statement'. If you don't see this option, your ABAP Development Tools in Eclipse version might be outdated.

Choosing this options copies an ABAP VALUE # statement to the clipboard. This generated statement is used to populate an internal table with the structure of table T100.

Now you can add this statement into your unit test class, or here simply into a notepad application:


VALUE # statement in notepad


This is great for database tables but often times you might also need the content of an internal table as an input for a method or function call.

To achieve this, we will create a little debugger script that can capture the content of an arbitrary internal table as a VALUE # statement.

First, create the following structure in the ABAP dictionary:


SE11 view of structure ZDEBUGGER_SCRIPTING_S


Or simply copy and paste the following structure definition in the ADT structure editor:
@EndUserText.label : 'Fields for Use in Debugger Scripts'
@AbapCatalog.enhancement.category : #EXTENSIBLE_CHARACTER_NUMERIC
define structure zdebugger_scripting_s {
itab : ortabname;
itab_from : fsline;
itab_to : feline;
all_table_fields : xfeld;

}

Next, go to transaction SAS to create the debugger script.




Replace the coding in the editor with the following code:
*---------------------------------------------------------------------*
* CLASS lcl_debugger_script DEFINITION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS lcl_debugger_script DEFINITION INHERITING FROM cl_tpda_script_class_super .

PUBLIC SECTION.
DATA gv_tabname TYPE tabname.
DATA gv_tab_from TYPE i.
DATA gv_tab_to TYPE i.
DATA gt_content TYPE tpda_scr_table_content_it.
DATA gt_components TYPE tpda_scr_table_comp_it.
DATA gv_all_fields TYPE xfeld.
DATA gv_field_cnt TYPE i.

METHODS prologue REDEFINITION.
METHODS init REDEFINITION.
METHODS script REDEFINITION.
METHODS end REDEFINITION.

METHODS get_table_fields.
METHODS build_val_pound_expression.

ENDCLASS. "lcl_debugger_script DEFINITION
*---------------------------------------------------------------------*
* CLASS lcl_debugger_script IMPLEMENTATION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS lcl_debugger_script IMPLEMENTATION.
METHOD prologue.
*** generate abap_source (source handler for ABAP)
super->prologue( ).
ENDMETHOD. "prolog

METHOD init.
DATA lt_sval TYPE STANDARD TABLE OF sval.
DATA lv_answer TYPE c LENGTH 1.
DATA lv_title TYPE string.
DATA ls_popup TYPE ZDEBUGGER_SCRIPTING_S. "Just for the where used list

IMPORT tabname TO gv_tabname FROM MEMORY ID sy-repid.

lv_title = 'Script Control Parameters'(000).

lt_sval = VALUE #( tabname = 'ZDEBUGGER_SCRIPTING_S'
( fieldname = 'ITAB' fieldtext = 'Internal Table Name'(001) value = gv_tabname field_obl = abap_true )
( fieldname = 'ALL_TABLE_FIELDS' fieldtext = 'Use all table fields'(002) value = abap_true )
( fieldname = 'ITAB_FROM' fieldtext = 'From record'(003) value = '1' )
( fieldname = 'ITAB_TO' fieldtext = 'To record'(004) value = '9999' )
).

CALL FUNCTION 'POPUP_GET_VALUES'
EXPORTING
popup_title = lv_title
IMPORTING
returncode = lv_answer
TABLES
fields = lt_sval
EXCEPTIONS
error_in_fields = 1
OTHERS = 2.

IF sy-subrc IS NOT INITIAL.
raise_error( ).
ENDIF.

IF lv_answer = 'A'.
* User canceled
raise_error( ).
ENDIF.

LOOP AT lt_sval INTO DATA(ls_sval).
CASE sy-tabix.
WHEN 1.
gv_tabname = ls_sval-value.
EXPORT tabname FROM gv_tabname TO MEMORY ID sy-repid.

WHEN 2.
gv_all_fields = ls_sval-value.

WHEN 3.
gv_tab_from = ls_sval-value.

WHEN 4.
gv_tab_to = ls_sval-value.

ENDCASE.

ENDLOOP.

ENDMETHOD. "init
METHOD script.
DATA lo_tab TYPE REF TO cl_tpda_script_tabledescr.
DATA lt_val TYPE tpda_scr_table_content_it.
DATA lt_comp TYPE tpda_scr_table_comp_it.
DATA lv_records TYPE i.

TRY.
lo_tab ?= cl_tpda_script_data_descr=>factory( CONV #( gv_tabname ) ).

lv_records = lo_tab->linecnt( ).

IF lv_records < gv_tab_to.
gv_tab_to = lv_records.
ENDIF.

lo_tab->content( EXPORTING p_line_from = gv_tab_from
p_line_to = gv_tab_to
IMPORTING p_it_comp_val = gt_content ).

gt_components = lo_tab->components( ).

get_table_fields( ).

build_val_pound_expression( ).

CATCH cx_root INTO DATA(lo_root).
RETURN.
ENDTRY.

ENDMETHOD. "script

METHOD get_table_fields.
DATA lt_sval TYPE STANDARD TABLE OF spopli.
DATA lv_answer TYPE c LENGTH 1.
DATA lv_title TYPE string.

gv_field_cnt = lines( gt_components ).

IF gv_all_fields = abap_true.
RETURN.
ENDIF.

lv_title = 'Select Table Fields'(003).

LOOP AT gt_components INTO DATA(ls_comp).
APPEND VALUE #( selflag = abap_true varoption = ls_comp-compname ) TO lt_sval.
ENDLOOP.

CALL FUNCTION 'POPUP_TO_DECIDE_LIST'
EXPORTING
mark_flag = abap_true
mark_max = 100
textline1 = 'Fieldname'(005)
titel = 'Select Table Fields'(006)
IMPORTING
answer = lv_answer
TABLES
t_spopli = lt_sval
EXCEPTIONS
not_enough_answers = 1
too_much_answers = 2
too_much_marks = 3
OTHERS = 4.

IF sy-subrc IS NOT INITIAL.
RETURN.
ENDIF.

IF lv_answer = 'A'.
* User canceled
RETURN.
ENDIF.

CLEAR gv_field_cnt.

LOOP AT lt_sval INTO DATA(ls_sval).
IF ls_sval-selflag IS INITIAL.
READ TABLE gt_components INDEX sy-tabix ASSIGNING FIELD-SYMBOL(<ls_comp>).
CHECK sy-subrc IS INITIAL.

CLEAR <ls_comp>-compname.
ELSE.
ADD 1 TO gv_field_cnt.
ENDIF.

ENDLOOP.

ENDMETHOD.

METHOD build_val_pound_expression.
DATA lv_max_fields_per_line TYPE n LENGTH 2 VALUE '10'.
DATA lv_max_field_length TYPE i.

DATA BEGIN OF ls_value.
DATA record TYPE c LENGTH 255.
DATA END OF ls_value.

DATA lt_value LIKE STANDARD TABLE OF ls_value.

LOOP AT gt_components INTO DATA(ls_comp).
DATA(lv_length) = strlen( ls_comp-compname ).

IF lv_length > lv_max_field_length.
lv_max_field_length = lv_length.
ENDIF.

ENDLOOP.

APPEND VALUE #( record = |{ gv_tabname CASE = LOWER } = VALUE #( | ) TO lt_value.

LOOP AT gt_content ASSIGNING FIELD-SYMBOL(<ls_tabrecord>).
IF gv_field_cnt > lv_max_fields_per_line.
APPEND VALUE #( record = '(' ) TO lt_value.
ELSE.
ls_value-record = '( '.
ENDIF.

LOOP AT <ls_tabrecord>-fields INTO DATA(lv_field).
READ TABLE gt_components INDEX sy-tabix INTO ls_comp.
CHECK sy-subrc IS INITIAL AND ls_comp-compname IS NOT INITIAL.

CASE ls_comp-typid.
WHEN 'D' OR 'T'.
CHECK lv_field CN '0 '.

WHEN 'I' OR 'N' OR 'P'.
CHECK lv_field CN '0., '.

WHEN 'C'.
CHECK lv_field <> space.

ENDCASE.

IF gv_field_cnt > lv_max_fields_per_line.
IF ls_comp-compname <> 'MANDT'.
ls_value-record = | { ls_comp-compname WIDTH = lv_max_field_length } = '{ lv_field }' |.
ELSE.
ls_value-record = | { ls_comp-compname WIDTH = lv_max_field_length } = sy-mandt |.
ENDIF.

APPEND ls_value TO lt_value.
ELSE.
IF ls_comp-compname <> 'MANDT'.
ls_value-record &&= | { ls_comp-compname } = '{ lv_field }' |.
ELSE.
ls_value-record &&= | { ls_comp-compname } = sy-mandt |.
ENDIF.
ENDIF.

ENDLOOP.
IF gv_field_cnt <= lv_max_fields_per_line
AND ls_value IS NOT INITIAL.
ls_value-record &&= ' )'.
APPEND ls_value TO lt_value.
ELSE.
APPEND VALUE #( record = ')' ) TO lt_value.
ENDIF.

CLEAR ls_value.
ENDLOOP.

APPEND VALUE #( record = |).| ) TO lt_value.

EDITOR-CALL FOR lt_value.

ENDMETHOD.

METHOD end.
*** insert your code which shall be executed at the end of the scripting (before trace is saved)
*** here

ENDMETHOD. "end
ENDCLASS. "lcl_debugger_script IMPLEMENTATION​


Save the debugger script under a new name:


Save Debugger Script


Make sure that the 'Trigger' for the script is set to 'Execute Directly'.

Let's test the script with a little sample program:
REPORT z_test_value_pound_script.

START-OF-SELECTION.
DATA lt_t100 TYPE TABLE OF t100.

SELECT FROM t100
FIELDS *
WHERE sprsl = @sy-langu
ORDER BY arbgb, msgnr
INTO TABLE @lt_t100
UP TO 1000 ROWS.

BREAK-POINT.

Execute the report.


Test report with data loaded from table T100


Switch to the 'Script' tab and load the newly created script:


Load Script ZRSTPDA_SCRIPT_VALUE_POUND


Start the script:


Start Script


The script will send a pop up window where you can specify the internal table name, here LT_T100, whether you want to use all of the fields from the table (Use all table fields = 'X' ) and which rows from the internal table you want to use.


Editor display of generated VALUE # statement


Now you can copy and paste the generated statement into your unit test class.

If you don't need all the fields from the table, then deselect the 'Use all table fields' indicator.



Do not want all the table fields and only 5 rows


In this case you get an extra pop up to select, which fields from the table you actually want:


Field Selection


And here the result:


Restricted Result Set


The script should work fine for internal tables with a flat structure. I haven't tested it with deep, nested structures.

In SAP S/4HANA on premise release 2022 this debugger script will be delivered under the name RSTPDA_SCRIPT_VALUE_POUND.

Example of test data in a unit test class generated with this script:


Generated data used in a test class method


Have fun generating test data for your unit tests with this little tool.
I hope you find it helpful. Let me know what you think.

Also check the question and answer section for ABAP 
and the ABAP community topics.
23 Comments