ABAP Unit Tests: Generate a VALUE # Statement for the Contents of an internal Table

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:



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.