This commit is contained in:
danrega
2024-12-23 13:00:14 +01:00
parent bc3eb31a8b
commit c8388b37d8
13 changed files with 3126 additions and 1266 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@
- [More (Special) Functions](#more-special-functions)
- [coalesce Function](#coalesce-function)
- [More Information](#more-information)
- [Executable Examples](#executable-examples)
This ABAP cheat sheet includes a variety of built-in functions in ABAP, along with code snippets to demonstrate their functionality. Many of the functions covered here are also included in other ABAP cheat sheets that focus on specific topics.
@@ -537,7 +538,7 @@ Search functions
<br><br>
``` abap
DATA(str) =`Pieces of cakes.`.
DATA(str) = `Pieces of cakes.`.
"---------------- find ----------------
"The find function searches for the substring specified and returns the offset
@@ -1690,13 +1691,6 @@ SELECT SINGLE
source_unit = unit`MI`,
target_unit = unit`KM` ) AS miles_to_km,
"Converts Euro to US dollars using today's rate
currency_conversion(
amount = d34n`1`,
source_currency = char`EUR`,
target_currency = char`USD`,
exchange_rate_date = @( cl_abap_context_info=>get_system_date( ) ) ) AS eur_to_usd,
"Creating a unique UUID for each row
uuid( ) AS uuid
@@ -1755,4 +1749,13 @@ SELECT tab2~key_field,
- [Built-in functions in ABAP](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbuilt_in_functions.htm)
- [Overview of functions](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenbuilt_in_functions_overview.htm)
- [Built-in functions that can be used by ABAP CDS and ABAP SQL](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_builtin_functions.htm)
- [Built-in functions that can be used by ABAP CDS and ABAP SQL](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenddic_builtin_functions.htm)
## Executable Examples
[zcl_demo_abap_builtin_func](./src/zcl_demo_abap_builtin_func.clas.abap)
> **💡 Note**<br>
> - The steps to import and run the code are outlined [here](README.md#-getting-started-with-the-examples).
> - [Disclaimer](./README.md#%EF%B8%8F-disclaimer)

View File

@@ -92,7 +92,7 @@ ABAP cheat sheets[^1] ...
|[Working with XML and JSON in ABAP](21_XML_JSON.md)|Covers processing XML using class libraries, XML transformations using XSLT and Simple Transformations (ST), serializations (ABAP to XML) and deserializations (XML to ABAP), dealing with JSON data|[zcl_demo_abap_xml_json](./src/zcl_demo_abap_xml_json.clas.abap)|
|[Released ABAP Classes](22_Released_ABAP_Classes.md)|Contains a selection of ABAP classes, serving as a quick introduction, along with code snippets to explore the functionality in action|- (The cheat sheet includes copy and paste code snippets and example classes)|
|[Date, Time, and Time Stamp](23_Date_and_Time.md)|Covers how to handle and process dates, times, and time stamps in ABAP|[zcl_demo_abap_date_time](./src/zcl_demo_abap_date_time.clas.abap)|
|[Built-In Functions](24_Builtin_Functions.md)|Covers a variety of built-in functions in ABAP|- (The cheat sheet includes copy and paste code snippets)|
|[Built-In Functions](24_Builtin_Functions.md)|Covers a variety of built-in functions in ABAP|[zcl_demo_abap_builtin_func](./src/zcl_demo_abap_builtin_func.clas.abap)|
|[Authorization Checks](25_Authorization_Checks.md)|Provides a high-level overview of explicit and implicit authorization checks in ABAP|- (The cheat sheet includes a copy and paste example class)|
|[ABAP Dictionary](26_ABAP_Dictionary.md)|Covers a selection of repository objects in the ABAP Dictionary (DDIC) that represent global types|- (The cheat sheet includes a copy and paste example class)|
|[Exceptions and Runtime Errors](27_Exceptions.md)|Provides an overview on exceptions and runtime errors|[zcl_demo_abap_error_handling](./src/zcl_demo_abap_error_handling.clas.abap)|

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<VSEOCLASS>
<CLSNAME>ZCL_DEMO_ABAP_BUILTIN_FUNC</CLSNAME>
<LANGU>E</LANGU>
<DESCRIPT>ABAP cheat sheet: Built-in Functions</DESCRIPT>
<STATE>1</STATE>
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
</VSEOCLASS>
</asx:values>
</asx:abap>
</abapGit>

View File

@@ -0,0 +1,67 @@
"! <p class="shorttext"><strong>Class Supporting ABAP Unit Test Example</strong><br/>ABAP cheat sheet example class</p>
"!
"! <p>The example class represents a dependent-on-component (DOC) and supports an ABAP Unit test example.
"! Methods of this class are called in another class: {@link zcl_demo_abap_unit_tdf}. The DOCs are replaced
"! by test doubles when running ABAP Unit tests.<br/>
"! Choose F9 in ADT to run the class.</p>
"!
"! <h2>Information</h2>
"! <p>Find information on getting started with the example class and the disclaimer in
"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.</p>
CLASS zcl_demo_abap_unit_dataprov DEFINITION
PUBLIC
CREATE PUBLIC .
PUBLIC SECTION.
METHODS get_discount RETURNING VALUE(discount) TYPE decfloat34.
METHODS get_discount_value IMPORTING day_value TYPE i
time_value TYPE i
RETURNING VALUE(discount_value) TYPE decfloat34.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap_unit_dataprov IMPLEMENTATION.
METHOD get_discount.
"Getting the weekday
"1) Monday, 2) Tuesday, 3) Wednesday, 4) Thursday, 5) Friday, 6) Saturday, 7) Sunday
DATA(weekday) = ( 5 + CONV d( xco_cp=>sy->date( xco_cp_time=>time_zone->utc
)->as( xco_cp_time=>format->iso_8601_basic )->value ) MOD 7 ) MOD 7 + 1.
"- Standard discount is granted at the weekend (Saturday, Sunday)
"- On other weekdays, discount is granted depending on the daytime
IF weekday = 6 OR weekday = 7.
discount = '20'.
ELSE.
"Retrieving the current time in UTC
DATA(utc_time) = CONV t( xco_cp=>sy->time( xco_cp_time=>time_zone->utc
)->as( xco_cp_time=>format->iso_8601_basic )->value ).
discount = COND #( WHEN utc_time BETWEEN '000000' AND '045959' THEN '15' "Night discount
WHEN utc_time BETWEEN '220000' AND '235959' THEN '15' "Night discount
WHEN utc_time BETWEEN '050000' AND '115959' THEN '10' "Morning discount
WHEN utc_time BETWEEN '180000' AND '215959' THEN '5' "Evening discount
ELSE 0 "No discount
).
ENDIF.
ENDMETHOD.
METHOD get_discount_value.
CASE day_value.
"Standard discount is granted at the weekend (Saturday, Sunday)
WHEN 6 OR 7.
discount_value = '20'.
"On other weekdays, discount is granted depending on the daytime
WHEN OTHERS.
discount_value = SWITCH #( time_value
WHEN 1 THEN '15' "Night discount
WHEN 2 THEN '10' "Morning discount
WHEN 3 THEN '5' "Evening discount
ELSE '0' "No discount
).
ENDCASE.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<VSEOCLASS>
<CLSNAME>ZCL_DEMO_ABAP_UNIT_DATAPROV</CLSNAME>
<LANGU>E</LANGU>
<DESCRIPT>ABAP cheat sheet: Built-in Functions</DESCRIPT>
<STATE>1</STATE>
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
</VSEOCLASS>
</asx:values>
</asx:abap>
</abapGit>

View File

@@ -0,0 +1,458 @@
"! <p class="shorttext"><strong>Creating Test Doubles Using ABAP Frameworks</strong><br/>ABAP cheat sheet example class</p>
"!
"! <p>The example class demonstrates managing dependencies (dependent-on-components, DOC) with ABAP Unit and
"! explores the creation of test doubles using ABAP frameworks. Additionally, the example explores test classes
"! not contained in the class to be tested, but rather in an external class.<br/>
"! Choose F9 in ADT to run the class. To run all unit tests of the class, choose Ctrl/Cmd + Shift + F10.<br/>
"! If the unit tests have not yet run, right-click the <em>Foreign Tests</em> entry in the ABAP Unit tab of ADT,
"! and choose <em>Run</em>.</p>
"!
"! <h2>Topics covered</h2>
"! <ul><li>ABAP OO Test Double Framework (test doubles injected using constructor and parameter injection)</li>
"! <li>ABAP SQL Test Double Framework</li>
"! <li>ABAP CDS Test Double Framework</li>
"! <li>Managing dependencies on RAP business objects (mocking ABAP EML APIs, transactional buffer test doubles)</li>
"! <li>This class does not include a test class in the Test Classes tab. The ABAP Unit example demonstrates the syntax
"! <em>&quot;! &#64;testing ...</em>. By using this syntax and specifying a class name, you can define a test relationship
"! between the test class and another class. Therefore, if you run unit tests for this class ({@link zcl_demo_abap_unit_tdf}),
"! it executes the test class within {@link zcl_demo_abap_unit_tdf}, which contains the syntax to establish the test
"! linkage.</li></ul>
"!
"! <h2>Running ABAP Unit tests</h2>
"! <ol><li>Open the class with the ABAP development tools for Eclipse (ADT).</li>
"! <li>Choose Ctrl/Cmd + Shift + F10 to launch all tests.
"! You can also right-click somewhere in the class and choose Run as -> ABAP Unit Test.
"! <li>As the test class is outside of this class in class {@link zcl_demo_abap_unit_tdf},
"! the ABAP Unit tab will show a <em>Foreign Tests</em> entry. If the unit tests have not run,
"! right click, the <em>Foreign Tests</em> entry, and choose <em>Run</em>.</li>
"! <li>The results of a test run are displayed in the ABAP Unit tab in ADT
"! and can be evaluated. The Failure Trace section provides information
"! on errors found.</li>
"! <li>If you are interested in test coverage, you can choose Ctrl/Cmd + Shift + F11,
"! or make a right-click, choose Run as -> ABAP Unit Test With..., select the Coverage
"! checkbox and choose Execute. You can then check the results in the ABAP Coverage tab,
"! what code is tested and what not.</li>
"! </ol>
"!
"! <h2>Information</h2>
"! <p>Find information on getting started with the example class and the disclaimer in
"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.</p>
CLASS zcl_demo_abap_unit_tdf DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
"--------------- 1) ABAP OO Test Double Framework ---------------
"The examples in the test include use the cl_abap_testdouble class.
"----------- 1a) Demonstrating constructor injection -----------
"Specifying the instance constructor with an optional parameter
"for the purpose of constructor injection as injection mechanism.
"In this example, methods are called from another, non-final class.
"They represent DOCs. Test doubles are injected when running unit
"tests. The parameter expects an instance of this class.
METHODS constructor
IMPORTING oref_constr_inj TYPE REF TO zcl_demo_abap_unit_dataprov OPTIONAL.
"Declaring an object reference variable that will be used to call
"methods of an external class (the DOC)
DATA oref_data_provider TYPE REF TO zcl_demo_abap_unit_dataprov.
"Method that is tested and contains a DOC. In this case, it is an
"external method that is called in the implementation part. The DOC
"is replaced by a test double created using cl_abap_testdouble.
METHODS td_constr_inj_calc_discount
IMPORTING
value TYPE numeric
RETURNING
VALUE(result) TYPE decfloat34.
"----------- 1b) Demonstrating parameter injection -----------
"Method that is tested and contains a DOC. In this case, it is an
"external method that is called in the implementation part. The DOC
"is replaced by a test double created using cl_abap_testdouble.
"The example demonstrates parameter injection. Therefore, an
"optional importing parameter is included. When running the unit
"test, 'data_prov' is bound, and the test double is injected.
METHODS td_param_inj_calc_discount
IMPORTING value TYPE numeric
date TYPE d
time TYPE t
data_prov TYPE REF TO zcl_demo_abap_unit_dataprov OPTIONAL
EXPORTING message TYPE string
weekday TYPE string
RETURNING VALUE(result) TYPE decfloat34.
"----------- 2) ABAP SQL Test Double Framework -----------
"The examples in the test include use the cl_osql_test_environment
"class. Here, a database table represents the DOC.
"The method includes a SELECT statement.
METHODS sql_get_shortest_flight_time IMPORTING carrier TYPE zdemo_abap_flsch-carrid
RETURNING VALUE(shortest_flight) TYPE i.
"----------- 3) ABAP CDS Test Double Framework -----------
"The examples in the test include use the cl_cds_get_data_set_environment class.
"Here, a CDS view entity represents the DOC. The method includes a SELECT
"statement. Another test method is implemented in the test inlcude that
"demonstrates the testing of a CDS view entity (without testing a method in the
"class under test).
METHODS cds_get_data_set IMPORTING carrier TYPE zdemo_abap_cds_ve_agg_exp-carrid
RETURNING VALUE(agg_line) TYPE zdemo_abap_cds_ve_agg_exp.
"----------- 4) Managing dependencies on RAP business objects -----------
"----------- 4a) Demonstrating mocking ABAP EML APIs --------------------
"----------- ABAP EML read operations -----------------------------------
"The examples in the test include use the cl_botd_mockemlapi_bo_test_env class.
"Populating database tables for ABAP EML read requests
METHODS prep_dbtab_for_eml IMPORTING read_op TYPE abap_boolean DEFAULT abap_false.
TYPES read_tab_ro TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_m.
TYPES read_tab_ch TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_m\_child.
"Method that includes an ABAP EML read request on the root entity
METHODS eml_read_root IMPORTING key TYPE zdemo_abap_rap_ro_m-key_field
RETURNING VALUE(tab_ro) TYPE read_tab_ro.
"Method that includes an ABAP EML read-by-associaton request
METHODS eml_rba IMPORTING key TYPE zdemo_abap_rap_ro_m-key_field
RETURNING VALUE(tab_ch) TYPE read_tab_ch.
"----------- ABAP EML modify operation -----------
TYPES modify_tab_ro TYPE TABLE FOR CREATE zdemo_abap_rap_ro_m.
TYPES ty_mapped TYPE RESPONSE FOR MAPPED EARLY zdemo_abap_rap_ro_m.
TYPES ty_failed TYPE RESPONSE FOR FAILED EARLY zdemo_abap_rap_ro_m.
TYPES ty_reported TYPE RESPONSE FOR REPORTED EARLY zdemo_abap_rap_ro_m.
"Method that includes an ABAP EML modify request on the root entity
METHODS eml_modify_root IMPORTING VALUE(instances) TYPE modify_tab_ro
EXPORTING mapped TYPE ty_mapped
failed TYPE ty_failed
reported TYPE ty_reported.
"----------- 4b) Demonstrating transactional buffer test doubles ---------
"The examples in the test include use the cl_botd_txbufdbl_bo_test_env class.
TYPES read_tab_ro_u TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_u.
"Method that includes an ABAP EML read request on the root entity
METHODS eml_read_root_buffer_td IMPORTING key TYPE zdemo_abap_rap_ro_u-key_field
RETURNING VALUE(tab_ro_u) TYPE read_tab_ro_u.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_demo_abap_unit_tdf IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
out->write( |ABAP Cheat Sheet Example: Creating Test Doubles Using ABAP Frameworks\n| ).
out->write( `*********************************************************************` ).
out->write( |* *| ).
out->write( `* ---> Choose Ctrl/Cmd + Shift + F10 to launch all unit tests <--- *` ).
out->write( |* *| ).
out->write( `* As the example is set up with a test class outside of this class *` ).
out->write( `* and if the unit tests have not yet run, right-click the Foreign *` ).
out->write( `* Tests entry in the ABAP Unit tab of ADT, and choose Run. *` ).
out->write( |* *| ).
out->write( |*********************************************************************\n\n| ).
"This implementation includes method calls of those methods that are unit tested
"in the test include.
"The implementation is just for demonstration purposes to explore the effect of
"the methods.
"Populating demo database tables (only required for running the example class
"with F9 and exploring the effect of various method calls)
zcl_demo_abap_aux=>fill_dbtabs( ).
"----- 1) Methods demonstrating the ABAP OO Test Double Framework in the test include -----
"----- 1a) Using constructor injection -----
DATA(td_constr_inj_calc_discount) = td_constr_inj_calc_discount( 100 ).
out->write( data = td_constr_inj_calc_discount name = `td_constr_inj_calc_discount` ).
out->write( |\n| ).
td_constr_inj_calc_discount = td_constr_inj_calc_discount( 500 ).
out->write( data = td_constr_inj_calc_discount name = `td_constr_inj_calc_discount` ).
out->write( |\n| ).
td_constr_inj_calc_discount = td_constr_inj_calc_discount( CONV decfloat34( '3.4' ) ).
out->write( data = td_constr_inj_calc_discount name = `td_constr_inj_calc_discount` ).
out->write( |\n| ).
"----- 1a) Using parameter injection -----
"Example with Sunday
td_param_inj_calc_discount(
EXPORTING
value = 100
date = '20241201'
time = '100000'
IMPORTING
weekday = DATA(weekday)
message = DATA(message)
RECEIVING
result = DATA(td_param_inj_calc_discount)
).
out->write( data = td_param_inj_calc_discount name = `td_param_inj_calc_discount` ).
out->write( data = weekday name = `weekday` ).
out->write( data = message name = `message` ).
out->write( |\n| ).
"Example with a weekday, morning
td_param_inj_calc_discount(
EXPORTING
value = 100
date = '20241202'
time = '100000'
IMPORTING
weekday = weekday
message = message
RECEIVING
result = td_param_inj_calc_discount
).
out->write( data = td_param_inj_calc_discount name = `td_param_inj_calc_discount` ).
out->write( data = weekday name = `weekday` ).
out->write( data = message name = `message` ).
out->write( |\n| ).
"Example with a weekday, afternoon
td_param_inj_calc_discount(
EXPORTING
value = 100
date = '20241203'
time = '150000'
IMPORTING
weekday = weekday
message = message
RECEIVING
result = td_param_inj_calc_discount
).
out->write( data = td_param_inj_calc_discount name = `td_param_inj_calc_discount` ).
out->write( data = weekday name = `weekday` ).
out->write( data = message name = `message` ).
out->write( |\n| ).
"Example with a weekday, night
td_param_inj_calc_discount(
EXPORTING
value = 100
date = '20241204'
time = '230000'
IMPORTING
weekday = weekday
message = message
RECEIVING
result = td_param_inj_calc_discount
).
out->write( data = td_param_inj_calc_discount name = `td_param_inj_calc_discount` ).
out->write( data = weekday name = `weekday` ).
out->write( data = message name = `message` ).
out->write( |\n| ).
"----- 2) Methods demonstrating the ABAP SQL Test Double Framework in the test include -----
DATA(sql_shortest_flight_time) = sql_get_shortest_flight_time( 'LH' ).
out->write( data = sql_shortest_flight_time name = `sql_shortest_flight_time` ).
out->write( |\n| ).
sql_shortest_flight_time = sql_get_shortest_flight_time( 'AA' ).
out->write( data = sql_shortest_flight_time name = `sql_shortest_flight_time` ).
out->write( |\n| ).
"----- 3) Methods demonstrating the ABAP CDS Test Double Framework in the test include -----
DATA(data_set_cds) = cds_get_data_set( 'LH' ).
out->write( data = data_set_cds name = `data_set_cds` ).
out->write( |\n\n| ).
data_set_cds = cds_get_data_set( 'AA' ).
out->write( data = data_set_cds name = `data_set_cds` ).
out->write( |\n\n| ).
"----- Methods demonstrating mocking ABAP EML APIs in the test include -----
"Populating demo database tables
prep_dbtab_for_eml( read_op = abap_true ).
DATA(tab_ro) = eml_read_root( key = 1 ).
out->write( tab_ro ).
out->write( |\n\n| ).
DATA(tab_ch) = eml_rba( key = 1 ).
out->write( tab_ch ).
out->write( |\n\n| ).
prep_dbtab_for_eml( ).
eml_modify_root(
EXPORTING
instances = VALUE #( ( %cid = `cid1` key_field = 3 field1 = 'aaa' field2 = 'bbb' field3 = 30 field4 = 300 )
( %cid = `cid2` key_field = 4 field1 = 'ccc' field2 = 'ddd' field3 = 40 field4 = 400 ) )
IMPORTING
mapped = DATA(m)
failed = DATA(f)
reported = DATA(r)
).
ASSERT f IS INITIAL.
ASSERT r IS INITIAL.
out->write( data = m-root name = `m-root` ).
out->write( |\n\n| ).
SELECT * FROM zdemo_abap_rapt1 INTO TABLE @DATA(itab).
out->write( data = itab name = `itab` ).
ENDMETHOD.
METHOD constructor.
"--------------------- 1a) ---------------------
"Demonstrating the constructor injection
"The parameter is only bound when you run the unit test.
"In that case, the test double is injected, and method calls
"in the implementations use data from the test double.
"Otherwise, a new instance is created that is used to call
"methods (a test double is not injected).
IF oref_constr_inj IS BOUND.
oref_data_provider = oref_constr_inj.
ELSE.
oref_data_provider = NEW #( ).
ENDIF.
ENDMETHOD.
METHOD td_constr_inj_calc_discount.
"--------------------- 1a) ---------------------
"Method that demonstrates the ABAP OO Test Double Framework and
"constructor injection in ABAP Unit.
"When running the unit test, 'oref_data_provider' includes the test
"double. The method expects a numeric value. 'get_discount' returns
"another numeric value on whose basis a discount calculation is
"performed. The value returned by the 'get_discount' method depends
"on the current weekday and the UTC time.
result = ( value * oref_data_provider->get_discount( ) ) / 100.
result = value - result.
ENDMETHOD.
METHOD td_param_inj_calc_discount.
"--------------------- 1b) ---------------------
"Method that demonstrates the ABAP OO Test Double Framework and
"parameter injection in ABAP Unit.
"When running the unit test, the optional parameter 'data_prov' is
"assigned, and therefore bound here. 'oref_data_provider' then includes
"the test double.
"The purpose of this method is similar to 'td_constr_inj_calc_discount'.
"Here, the method expects a date and time besides a numeric value.
IF data_prov IS BOUND.
oref_data_provider = data_prov.
ENDIF.
"Getting the weekday
DATA(day_value) = ( 5 + date MOD 7 ) MOD 7 + 1.
weekday = SWITCH #( day_value
WHEN 1 THEN `Monday`
WHEN 2 THEN `Tuesday`
WHEN 3 THEN `Wednesday`
WHEN 4 THEN `Thursday`
WHEN 5 THEN `Friday`
WHEN 6 THEN `Saturday`
WHEN 7 THEN `Sunday` ).
DATA(time_value) = COND #( WHEN time BETWEEN '000000' AND '045959' THEN 1 "Night discount
WHEN time BETWEEN '220000' AND '235959' THEN 1 "Night discount
WHEN time BETWEEN '050000' AND '115959' THEN 2 "Morning discount
WHEN time BETWEEN '180000' AND '215959' THEN 3 "Evening discount
ELSE 0 "No discount
).
weekday = |{ weekday } ({ SWITCH #( time_value WHEN 1 THEN `night` WHEN 2 THEN `morning` WHEN 3 THEN `evening` ELSE `afternoon` ) })|.
DATA(disc) = oref_data_provider->get_discount_value( day_value = day_value time_value = time_value ).
result = ( value * disc ) / 100.
result = value - result.
message = |Original value: { value }; discount: { disc }; value with discount: { result }; | &&
|when: { weekday }, { date DATE = ISO }, { time TIME = ISO }|.
ENDMETHOD.
METHOD sql_get_shortest_flight_time.
"--------------------- 2) ---------------------
"When running the unit test, the DOC (database table) is replaced with a test double.
"Getting the shortest flight time among a given carrier
SELECT MIN( fltime ) AS fltime FROM zdemo_abap_flsch WHERE carrid = @carrier INTO @shortest_flight.
ENDMETHOD.
METHOD cds_get_data_set.
"--------------------- 3) ---------------------
"When running the unit test, the DOC (CDS view entity) is replaced with a test double.
"Getting a line filtered by the carrier
SELECT SINGLE * FROM zdemo_abap_cds_ve_agg_exp WHERE carrid = @carrier INTO @agg_line.
ENDMETHOD.
METHOD prep_dbtab_for_eml.
"Preparing database tables for ABAP EML statements
DELETE FROM zdemo_abap_rapt1.
IF read_op = abap_true.
DELETE FROM zdemo_abap_rapt2.
INSERT zdemo_abap_rapt1 FROM TABLE @( VALUE #( ( key_field = 1 field1 = 'aaa' field2 = 'bbb' field3 = 10 field4 = 100 ) ) ).
INSERT zdemo_abap_rapt2 FROM TABLE @( VALUE #( ( key_field = 1 key_ch = 11 field_ch1 = 'ccc' field_ch2 = 111 )
( key_field = 1 key_ch = 12 field_ch1 = 'ddd' field_ch2 = 112 ) ) ).
ENDIF.
ENDMETHOD.
METHOD eml_read_root.
"--------------------- 4a) ---------------------
"Method that includes an ABAP EML read request on the root entity
"When running the unit test, the ABAP EML API is mocked.
READ ENTITIES OF zdemo_abap_rap_ro_m
ENTITY root
ALL FIELDS WITH VALUE #( ( key_field = key ) )
RESULT tab_ro.
ENDMETHOD.
METHOD eml_rba.
"--------------------- 4a) ---------------------
"Method that includes an ABAP EML read-by-assocation request
"When running the unit test, the ABAP EML API is mocked.
READ ENTITIES OF zdemo_abap_rap_ro_m
ENTITY root
BY \_child
ALL FIELDS WITH VALUE #( ( key_field = key ) )
RESULT tab_ch.
ENDMETHOD.
METHOD eml_modify_root.
"--------------------- 4a) ---------------------
"Method that includes an ABAP EML create request
"When running the unit test, the ABAP EML API is mocked.
MODIFY ENTITIES OF zdemo_abap_rap_ro_m
ENTITY root
CREATE
FIELDS ( key_field field1 field2 field3 field4 )
WITH instances
MAPPED mapped
FAILED failed
REPORTED reported.
COMMIT ENTITIES.
ENDMETHOD.
METHOD eml_read_root_buffer_td.
"--------------------- 4b) ---------------------
"Method that includes an ABAP EML read request
"When running the unit test, a transactional buffer test double is
"included.
READ ENTITIES OF zdemo_abap_rap_ro_u
ENTITY root
ALL FIELDS WITH VALUE #( ( key_field = key ) )
RESULT tab_ro_u.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<VSEOCLASS>
<CLSNAME>ZCL_DEMO_ABAP_UNIT_TDF</CLSNAME>
<LANGU>E</LANGU>
<DESCRIPT>ABAP cheat sheet: Creating Test Doubles Using ABAP Framework</DESCRIPT>
<STATE>1</STATE>
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
</VSEOCLASS>
</asx:values>
</asx:abap>
</abapGit>

View File

@@ -47,6 +47,7 @@ CLASS zcl_demo_abap_xml_json DEFINITION
deserialize_helper IMPORTING attr_string_a TYPE string
attr_string_b TYPE string
attr_concat_string TYPE string.
ENDCLASS.
@@ -144,7 +145,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
( ReleasedObjectName LIKE '%IXML%' OR ReleasedObjectName LIKE '%SXML%' )
INTO TABLE @DATA(released_xml_libs).
out->write( `No output. You can check the internal table content in the debugger to view the usable artifacts.` ).
out->write( zcl_demo_abap_aux=>no_output ).
***********************************************************************
@@ -196,29 +197,27 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
***********************************************************************
out->write( zcl_demo_abap_aux=>heading( `4) Parsing XML Data Using iXML` ) ).
"The example covers the following aspects:
"- Parsing XML data to a DOM object in one go
"- Directly reading nodes using various iXML methods
"- Directly reading nodes using element names
"- Reading using iterators, i.e. going over the XML nodes one after another
"- During the iteration, ...
" ... node properties are extracted using various iXML methods
" (and stored in an internal table for display purposes).
" ... the XML data is modified.
" ... a new element is created.
"- Rendering XML data
"Notes on the example:
"- XML data is transformed to an input steam object and imported to a DOM object in one go using a parser object
"- If the XML is successfully parsed, DOM object nodes are iterated and accessed
"- Elements of the XML data and their attributes are processed, read and output
"- The create_renderer method is used to render XML data into an output stream
"Internal table to store node properties for display purposes
DATA properties TYPE string_table.
"Creating simple demo XML data to be used in the example
TRY.
DATA(some_xml) = cl_abap_conv_codepage=>create_out( )->convert(
`<hi>` &&
` <word1>hallo</word1>` &&
` <word2>how</word2>` &&
` <word3>are</word3>` &&
`</hi>` ).
`<?xml version="1.0"?>` &&
`<node attr_a="123">` &&
` <subnode1>` &&
` <letter>A</letter>` &&
` <date format="mm-dd-yyyy">01-01-2024</date>` &&
` </subnode1>` &&
` <subnode2>` &&
` <text attr_b="1" attr_c="a">abc</text>` &&
` <text attr_b="2" attr_c="b">def</text>` &&
` <text attr_b="3" attr_c="c">ghi</text>` &&
` <text attr_b="4" attr_c="d">jkl</text>` &&
` </subnode2>` &&
`</node>` ).
CATCH cx_sy_conversion_codepage.
ENDTRY.
@@ -237,77 +236,59 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
document = document_pa
stream_factory = stream_factory_pa ).
"Parsing XML data to a DOM representation in one go. It is put in the memory.
"Note: You can also parse sequentially, and not in one go.
DATA(parsing_check) = parser_pa->parse( ).
IF parsing_check = 0. "Parsing was successful
IF parser_pa->parse( ) <> 0.
"Processing errors
DATA(error_pa_num) = parser_pa->num_errors( ).
"Directly reading nodes using various iXML methods
"Accessing the root element of the DOM. It can be used as the initial node
"for accessing subnodes.
"You can check the content of the variables in the debugger.
"Note: Multiple methods are available to further process the nodes.
DATA(root_element) = document_pa->get_root_element( ).
"First subnode
DATA(child_element) = root_element->get_first_child( ).
"Getting the value of that node
DATA(child_element_value) = child_element->get_value( ).
"Next adjacent node/getting the value
DATA(next_element_value) = child_element->get_next( )->get_value( ).
"Directly reading nodes using element names
"The result is the first element searched for.
DATA(element_by_name) = document_pa->find_from_name( name = `word3` )->get_value( ).
"A lot more options are available such as access by attributes.
"Reading using iterators, i.e. going over the XML nodes sequentially
"Creating an iterator
DATA(iterator_pa) = document_pa->create_iterator( ).
DO.
"For the iteration, you can use the get_next method to process the nodes one after another.
"Note: Here, all nodes are respected. You can also create filters to go over specific nodes.
DATA(node_i) = iterator_pa->get_next( ).
IF node_i IS INITIAL.
EXIT.
ELSE.
"Extracting properties
"For display purposes, the properties are stored in an internal table.
APPEND |gid: { node_i->get_gid( ) } / type: { node_i->get_type( ) } / name: { node_i->get_name( ) } / value: { node_i->get_value( ) }| TO properties.
ENDIF.
IF node_i->get_type( ) = if_ixml_node=>co_node_text.
"Modifying values
"Here, the values are capitalized.
node_i->set_value( to_upper( node_i->get_value( ) ) ).
ENDIF.
"Creating a new element
IF node_i->get_value( ) = 'are'.
document_pa->create_simple_element_ns( name = 'word4'
value = 'you'
parent = node_i->get_parent( ) ).
ENDIF.
DO error_pa_num TIMES.
DATA(error_pa) = parser_pa->get_error(
index = sy-index - 1
min_severity = 3
).
DATA(reason_pa) = error_pa->get_reason( ).
out->write( |Error: { reason_pa }| ).
ENDDO.
"Creating a renderer
DATA xml_pa TYPE xstring.
ixml_pa->create_renderer( document = document_pa
ostream = ixml_pa->create_stream_factory( )->create_ostream_xstring( string = xml_pa )
)->render( ).
"Getting XML
DATA(output_ixml_parsing) = format( cl_abap_conv_codepage=>create_in( )->convert( xml_pa ) ).
out->write( output_ixml_parsing ).
out->write( |\n| ).
out->write( `Node properties:` ).
out->write( properties ).
ELSE.
out->write( `Parsing was not successful.` ).
"Processing document
IF document_pa IS NOT INITIAL.
DATA(iterator_pa) = document_pa->create_iterator( ).
DATA(node_pa) = iterator_pa->get_next( ).
WHILE NOT node_pa IS INITIAL.
DATA(indent) = node_pa->get_height( ) * 2.
"Rettrieving the node type
CASE node_pa->get_type( ).
WHEN if_ixml_node=>co_node_element.
"Retrieving attributes
DATA(attributes_pa) = node_pa->get_attributes( ).
out->write( |Element:{ repeat( val = ` ` occ = indent + 2 ) }{ node_pa->get_name( ) }| ).
IF NOT attributes_pa IS INITIAL.
DO attributes_pa->get_length( ) TIMES.
DATA(attr) = attributes_pa->get_item( sy-index - 1 ).
out->write( |Attribute:{ repeat( val = ` ` occ = indent ) }{ attr->get_name( ) } = { attr->get_value( ) } | ).
ENDDO.
ENDIF.
WHEN if_ixml_node=>co_node_text OR
if_ixml_node=>co_node_cdata_section.
out->write( |Text:{ repeat( val = ` ` occ = indent + 5 ) }{ node_pa->get_value( ) }| ).
ENDCASE.
"Retrieving the next node
node_pa = iterator_pa->get_next( ).
ENDWHILE.
ENDIF.
ENDIF.
"Creating a renderer
DATA xml_pa TYPE xstring.
ixml_pa->create_renderer( document = document_pa
ostream = ixml_pa->create_stream_factory( )->create_ostream_xstring( string = xml_pa )
)->render( ).
"Getting XML
DATA(output_ixml_parsing) = cl_abap_conv_codepage=>create_in( )->convert( xml_pa ).
out->write( |\n| ).
out->write( output_ixml_parsing ).
***********************************************************************
out->write( zcl_demo_abap_aux=>heading( `5) Creating XML Data Using sXML (Token-Based Rendering)` ) ).
@@ -426,13 +407,9 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
"Creating an internal table for display purposes
DATA: BEGIN OF node_info,
node_type TYPE string,
prefix TYPE string,
name TYPE string,
nsuri TYPE string,
value_type TYPE string,
value TYPE string,
value_raw TYPE xstring,
node_type TYPE string,
name TYPE string,
value TYPE string,
END OF node_info,
nodes_tab LIKE TABLE OF node_info.
@@ -446,8 +423,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
"To iterate accros all nodes, you can call the NEXT_NODE method.
TRY.
DO.
"Check out other available methods in ADT by placing the cursor behind ->
"and choosing CTRL + Space.
CLEAR node_info.
reader->next_node( ).
"When reaching the end of the XML data, the loop is exited.
@@ -457,39 +433,21 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
"You can access the properties of the node directly.
"For display purposes, the property information is stored in an internal table.
"The demo XML data that is used here does not include all properties. Therefore,
"the values for these are initial.
"The example here just uses simple demo JSON data. Not all properties are
"retrieved and displayed.
"Node type, see the interface if_sxml_node
DATA(node_type) = SWITCH #( reader->node_type WHEN if_sxml_node=>co_nt_initial THEN `CO_NT_INITIAL`
WHEN if_sxml_node=>co_nt_element_open THEN `CO_NT_ELEMENT_OPEN`
WHEN if_sxml_node=>co_nt_element_close THEN `CO_NT_ELEMENT_CLOSE`
WHEN if_sxml_node=>co_nt_value THEN `CO_NT_VALUE`
WHEN if_sxml_node=>co_nt_attribute THEN `CO_NT_ATTRIBUTE`
ELSE `Error` ).
DATA(prefix) = reader->prefix. "Namespace prefix
DATA(name) = reader->name. "Name of the element
DATA(nsuri) = reader->nsuri. "Namespace URI
"Value type, see the interface if_sxml_value
DATA(value_type) = SWITCH #( reader->value_type WHEN 0 THEN `Initial`
WHEN if_sxml_value=>co_vt_none THEN `CO_VT_NONE`
WHEN if_sxml_value=>co_vt_text THEN `CO_VT_TEXT`
WHEN if_sxml_value=>co_vt_raw THEN `CO_VT_RAW`
WHEN if_sxml_value=>co_vt_any THEN `CO_VT_ANY`
ELSE `Error` ).
DATA(value) = reader->value. "Character-like value (if it is textual data)
DATA(value_raw) = reader->value_raw. "Byte-like value (if it is raw data)
APPEND VALUE #( node_type = node_type
prefix = prefix
name = name
nsuri = nsuri
value_type = value_type
value = value
value_raw = value_raw ) TO nodes_tab.
node_info-node_type = SWITCH #( reader->node_type WHEN if_sxml_node=>co_nt_initial THEN `CO_NT_INITIAL`
WHEN if_sxml_node=>co_nt_element_open THEN `CO_NT_ELEMENT_OPEN`
WHEN if_sxml_node=>co_nt_element_close THEN `CO_NT_ELEMENT_CLOSE`
WHEN if_sxml_node=>co_nt_value THEN `CO_NT_VALUE`
WHEN if_sxml_node=>co_nt_attribute THEN `CO_NT_ATTRIBUTE`
ELSE `Error` ).
"Name of the element
node_info-name = reader->name.
"Character-like value (if it is textual data)
node_info-value = COND #( WHEN reader->node_type = if_sxml_node=>co_nt_value THEN reader->value ).
APPEND node_info TO nodes_tab.
"Once the method is called, you can directly access the attributes of the reader with the required
"properties of the node. When the parser is on the node of an element opening, you can use the method
@@ -500,22 +458,18 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
IF reader->node_type <> if_sxml_node=>co_nt_attribute.
EXIT.
ENDIF.
APPEND VALUE #( node_type = `attribute`
prefix = reader->prefix
APPEND VALUE #( node_type = `CO_NT_ATTRIBUTE`
name = reader->name
nsuri = reader->nsuri
value = reader->value
value_raw = reader->value_raw ) TO nodes_tab.
value = reader->value ) TO nodes_tab.
ENDDO.
ENDIF.
ENDDO.
out->write( nodes_tab ).
CATCH cx_sxml_state_error INTO DATA(error_parse_token).
out->write( error_parse_token->get_text( ) ).
ENDTRY.
out->write( `Node properties:` ).
out->write( nodes_tab ).
***********************************************************************
out->write( zcl_demo_abap_aux=>heading( `8) Parsing XML Data using sXML (Object-Oriented Parsing)` ) ).
@@ -551,20 +505,15 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
DATA(open_element) = CAST if_sxml_open_element( node_oo ).
APPEND VALUE #( node_type = `open element`
prefix = open_element->prefix
name = open_element->qname-name
nsuri = open_element->qname-namespace
) TO nodes_tab.
DATA(attributes) = open_element->get_attributes( ).
LOOP AT attributes INTO DATA(attribute).
APPEND VALUE #( node_type = `attribute`
prefix = open_element->prefix
name = open_element->qname-name
nsuri = open_element->qname-namespace
value = SWITCH #( attribute->value_type WHEN if_sxml_value=>co_vt_text THEN attribute->get_value( ) )
value_raw = SWITCH #( attribute->value_type WHEN if_sxml_value=>co_vt_raw THEN attribute->get_value_raw( ) )
) TO nodes_tab.
ENDLOOP.
@@ -572,9 +521,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
DATA(close_element) = CAST if_sxml_close_element( node_oo ).
APPEND VALUE #( node_type = `close element`
prefix = open_element->prefix
name = open_element->qname-name
nsuri = open_element->qname-namespace
) TO nodes_tab.
WHEN if_sxml_node=>co_nt_value.
@@ -582,7 +529,6 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
APPEND VALUE #( node_type = `value`
value = SWITCH #( value_node_oo->value_type WHEN if_sxml_value=>co_vt_text THEN value_node_oo->get_value( ) )
value_raw = SWITCH #( value_node_oo->value_type WHEN if_sxml_value=>co_vt_raw THEN value_node_oo->get_value_raw( ) )
) TO nodes_tab.
WHEN OTHERS.
@@ -593,7 +539,6 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
out->write( error_parse_oo->get_text( ) ).
ENDTRY.
out->write( `Node properties:` ).
out->write( nodes_tab ).
***********************************************************************
@@ -1223,7 +1168,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
************************************************************************
out->write( zcl_demo_abap_aux=>heading( `22) Dealing with JSON Data` ) ).
out->write( zcl_demo_abap_aux=>heading( `22) Transforming JSON Data Using Transformations` ) ).
"Note: When the identity transformation ID is used, the format is asJSON.
"Elementary type
@@ -1391,18 +1336,18 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
************************************************************************
out->write( zcl_demo_abap_aux=>heading( `23) XCO Classes for JSON` ) ).
out->write( zcl_demo_abap_aux=>heading( `23) Handling JSON Data with XCO Classes` ) ).
"Note: Unlike above, the following snippets do not work with asJSON as intermediate
"format.
DATA: BEGIN OF carrier_struc,
carrier_id TYPE c length 3,
connection_id TYPE n length 4,
city_from TYPE c length 20,
city_to TYPE c length 20,
carrier_id TYPE c LENGTH 3,
connection_id TYPE n LENGTH 4,
city_from TYPE c LENGTH 20,
city_to TYPE c LENGTH 20,
END OF carrier_struc.
DATA carriers_tab like TABLE OF carrier_struc WITH EMPTY KEY.
DATA carriers_tab LIKE TABLE OF carrier_struc WITH EMPTY KEY.
carrier_struc = VALUE #( carrier_id = 'AA' connection_id = '17' city_from = 'New York' city_to = 'San Francisco' ).
carriers_tab = VALUE #( ( carrier_id = 'AZ' connection_id = '788' city_from = 'Rome' city_to = 'Tokyo' )
@@ -1467,7 +1412,131 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
************************************************************************
out->write( zcl_demo_abap_aux=>heading( `24) Excursion: Compressing and Decompressing Binary Data` ) ).
out->write( zcl_demo_abap_aux=>heading( `24) Handling JSON Data with the /ui2/cl_json Class` ) ).
TYPES: BEGIN OF demo_struc,
carrier_id TYPE c LENGTH 3,
connection_id TYPE n LENGTH 4,
city_from TYPE c LENGTH 20,
city_to TYPE c LENGTH 20,
END OF demo_struc.
DATA itab TYPE TABLE OF demo_struc WITH EMPTY KEY.
itab = VALUE #( ( carrier_id = 'AA' connection_id = '0017' city_from = 'New York' city_to = 'San Francisco' )
( carrier_id = 'AZ' connection_id = '0789' city_from = 'Tokyo' city_to = 'Rome' ) ).
"---------------- Serializing ----------------
DATA(abap_to_json) = /ui2/cl_json=>serialize( data = itab ).
"Note the many additional, optional parameters such as for formatting the
"serialized JSON. For more information, see the class documentation.
DATA(abap_to_json_pretty) = /ui2/cl_json=>serialize( data = itab
format_output = abap_true ).
DATA(abap_to_json_pretty_name) = /ui2/cl_json=>serialize( data = itab
format_output = abap_true
pretty_name = /ui2/cl_json=>pretty_mode-camel_case ).
out->write( `---------- ABAP -> JSON ----------` ).
out->write( abap_to_json ).
out->write( |\n| ).
out->write( `---------- ABAP -> JSON (pretty printed) ----------` ).
out->write( abap_to_json_pretty ).
out->write( |\n| ).
out->write( `---------- ABAP -> JSON (camel case) ----------` ).
out->write( abap_to_json_pretty_name ).
out->write( |\n| ).
"---------------- Deserializing ----------------
DATA(json_to_abap) = abap_to_json.
DATA itab_json_to_abap LIKE itab.
/ui2/cl_json=>deserialize( EXPORTING json = json_to_abap
CHANGING data = itab_json_to_abap ).
out->write( `---------- JSON -> ABAP ----------` ).
out->write( itab_json_to_abap ).
out->write( |\n| ).
"---------------- Deserializing: Applying name mapping ----------------
"Creating an internal table with different field names
TYPES: BEGIN OF demo_struc4map,
carr TYPE c LENGTH 3,
conn TYPE n LENGTH 4,
from TYPE c LENGTH 20,
to TYPE c LENGTH 20,
END OF demo_struc4map.
DATA itab4map TYPE TABLE OF demo_struc4map WITH EMPTY KEY.
/ui2/cl_json=>deserialize( EXPORTING json = json_to_abap
name_mappings = VALUE #( ( abap = 'CARR' json = `CARRIER_ID` )
( abap = 'CONN' json = `CONNECTION_ID` )
( abap = 'FROM' json = `CITY_FROM` )
( abap = 'TO' json = `CITY_TO` ) )
CHANGING data = itab4map ).
out->write( `---------- JSON -> ABAP (Name mapping) ----------` ).
out->write( itab4map ).
out->write( |\n| ).
"---------------- Deserializing: Using JSON as xstring ----------------
DATA(json_xstring) = cl_abap_conv_codepage=>create_out( )->convert(
`[` &&
` {` &&
` "carrier_id": "LH",` &&
` "connection_id": "400",` &&
` "city_from": "Frankfurt",` &&
` "city_to": "Berlin"` &&
` },` &&
` {` &&
` "carrier_id": "DL",` &&
` "connection_id": "1984",` &&
` "city_from": "San Francisco",` &&
` "city_to": "New York"` &&
` },` &&
` {` &&
` "carrier_id": "AZ",` &&
` "connection_id": "790",` &&
` "city_from": "Rome",` &&
` "city_to": "Osaka"` &&
` }` &&
`]` ).
DATA itab_json_xstr_to_abap LIKE itab.
/ui2/cl_json=>deserialize( EXPORTING jsonx = json_xstring
CHANGING data = itab_json_xstr_to_abap ).
out->write( `---------- JSON (xstring) -> ABAP ----------` ).
out->write( itab_json_xstr_to_abap ).
out->write( |\n| ).
"---------------- Deserializing: No equivalent ABAP type available ----------------
"The example assumes that there is no equivalent ABAP type available for JSON data
"that is to be deserialized. You can use the 'generate' method that has a
"returning parameter of the generic type 'ref to data'.
DATA(json) = `[{"CARRIER_ID":"AA","CONNECTION_ID":17,"CITY_FROM":"New York","CITY_TO":"San Francisco"},` &&
`{"CARRIER_ID":"AZ","CONNECTION_ID":789,"CITY_FROM":"Tokyo","CITY_TO":"Rome"}]`.
DATA(dref) = /ui2/cl_json=>generate( json = json ).
DATA(dref_xstr) = /ui2/cl_json=>generate( jsonx = json_xstring ).
"You can further process the content, for example, with RTTS as outlined in the
"Dynamic Programming cheat sheet.
IF dref IS BOUND.
out->write( `---------- JSON -> ABAP (unknown type) ----------` ).
out->write( dref->* ).
out->write( |\n| ).
ENDIF.
IF dref_xstr IS BOUND.
out->write( `---------- JSON (xstring) -> ABAP (unknown type) ----------` ).
out->write( dref_xstr->* ).
ENDIF.
************************************************************************
out->write( zcl_demo_abap_aux=>heading( `25) Excursion: Compressing and Decompressing Binary Data` ) ).
"You may want to process or store binary data. The data can be very large.
"You can compress the data in gzip format and decompress it for further processing using
"the cl_abap_gzip class. Check out appropriate exceptions to be caught. The simple example
@@ -1503,6 +1572,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
IF xml_oref_a = xstr_decomp.
out->write( `The decompressed binary data object has the same value as the original binary data object.` ).
ENDIF.
ENDMETHOD.
METHOD format.
TRY.

View File

@@ -0,0 +1,25 @@
"! <p class="shorttext"><strong>Test Class Supporting an ABAP Unit Test Example</strong><br/>ABAP cheat sheet example class</p>
"!
"! <p>The example class {@link zcl_demo_abap_unit_tdf} demonstrates managing dependencies (dependent-on-components, DOC)
"! with ABAP Unit and explores the creation of test doubles using ABAP frameworks.<br/>
"! This global class is empty, but the the <em>Test Classes</em> tab includes a test class. Using the syntax
"! <em>&quot;! &#64;testing ...</em>, a test relation between this test class and another global class
"! ({@link zcl_demo_abap_unit_tdf}) is defined. Therefore, when you run unit tests of class {@link zcl_demo_abap_unit_tdf},
"! the tests contained in the class {@link ztcl_demo_abap_unit_tdf_testcl} are executed.</p>
"!
"! <h2>Information</h2>
"! <p>Find information on getting started with the example class and the disclaimer in
"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.</p>
CLASS ztcl_demo_abap_unit_tdf_testcl DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS ztcl_demo_abap_unit_tdf_testcl IMPLEMENTATION.
ENDCLASS.

View File

@@ -0,0 +1,551 @@
"!@testing zcl_demo_abap_unit_tdf
CLASS ltcl_test DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
"------------------- Local test class -------------------
"Object reference variable for class under test
CLASS-DATA cut TYPE REF TO zcl_demo_abap_unit_tdf.
"----- 1) ABAP OO Test Double Framework ------
"Test method that demonstrates constructor injection
METHODS test_tdf_constr_inj_get_discnt FOR TESTING.
"Test method that demonstrates parameter injection
METHODS test_tdf_param_inj_get_discnt FOR TESTING.
"----------- 2) ABAP SQL Test Double Framework -----------
METHODS test_sql_get_shortest_flight FOR TESTING RAISING cx_static_check.
CLASS-DATA sql_env TYPE REF TO if_osql_test_environment.
"----------- 3) ABAP CDS Test Double Framework-----------
METHODS test_select_cds FOR TESTING RAISING cx_static_check.
METHODS test_cds_standalone FOR TESTING RAISING cx_static_check.
METHODS prepare_testdata_set_cds.
CLASS-DATA cds_env TYPE REF TO if_cds_test_environment.
DATA zdemo_abap_fli_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY.
"----------- 4) Managing dependencies on RAP business objects -----------
CONSTANTS entity TYPE abp_entity_name VALUE 'ZDEMO_ABAP_RAP_RO_M'.
CLASS-DATA eml_env TYPE REF TO if_botd_mockemlapi_bo_test_env.
"--- 4a) Test methods that demonstrate mocking EML APIs ---
METHODS test_eml_read_root FOR TESTING.
METHODS test_eml_rba FOR TESTING.
METHODS test_eml_modify_root FOR TESTING.
DATA td_eml TYPE REF TO if_botd_mockemlapi_test_double.
"--- 4b) Test method that demonstrates transactional buffer test doubles ---
METHODS test_eml_read_root_buffer_td FOR TESTING.
CLASS-DATA buffer_env TYPE REF TO if_botd_txbufdbl_bo_test_env.
"----------- Test fixture -----------
"Creating a common start state for each test method, clearing doubles
METHODS setup RAISING cx_static_check.
"Includes the creation of test environments
CLASS-METHODS class_setup RAISING cx_static_check.
"Clearing generated test doubles
CLASS-METHODS class_teardown.
ENDCLASS.
CLASS ltcl_test IMPLEMENTATION.
METHOD class_setup.
"Creating an instance for the class under test
cut = NEW zcl_demo_abap_unit_tdf( ).
"Creating instances of test environments for the test execution
"------ 2) SQL ------
sql_env = cl_osql_test_environment=>create(
i_dependency_list = VALUE #( ( 'zdemo_abap_flsch' ) ) ).
"------ 3) CDS ------
cds_env = cl_cds_test_environment=>create( i_for_entity = 'zdemo_abap_cds_ve_agg_exp'
test_associations = 'X' ).
"------ 4) ABAP EML ------
"4a) Mocking ABAP EML APIs
eml_env = cl_botd_mockemlapi_bo_test_env=>create(
environment_config = cl_botd_mockemlapi_bo_test_env=>prepare_environment_config(
)->set_bdef_dependencies( bdef_dependencies = VALUE #( ( 'ZDEMO_ABAP_RAP_RO_M' ) ) ) ).
"4b) Transactional buffer test doubles
buffer_env = cl_botd_txbufdbl_bo_test_env=>create(
environment_config = cl_botd_txbufdbl_bo_test_env=>prepare_environment_config(
)->set_bdef_dependencies( bdef_dependencies = VALUE #( ( 'ZDEMO_ABAP_RAP_RO_U' ) ) ) ).
ENDMETHOD.
METHOD class_teardown.
"Clearing generated test double at the end of the test execution
sql_env->destroy( ).
cds_env->destroy( ).
eml_env->destroy( ).
buffer_env->destroy( ).
ENDMETHOD.
METHOD setup.
sql_env->clear_doubles( ).
cds_env->clear_doubles( ).
eml_env->clear_doubles( ).
buffer_env->clear_doubles( ).
ENDMETHOD.
METHOD test_sql_get_shortest_flight.
"--- 2) ---
"Preparing and inserting test data
DATA test_data TYPE TABLE OF zdemo_abap_flsch WITH EMPTY KEY.
test_data = VALUE #(
( carrid = 'LH'
connid = 0401
fltime = 435 )
( carrid = 'LH'
connid = 0402
fltime = 455 )
( carrid = 'LH'
connid = 2402
fltime = 65 ) ).
sql_env->insert_test_data( test_data ).
"Calling method of the class under test
DATA carrier TYPE zdemo_abap_flsch-carrid VALUE 'LH'.
DATA(result) = cut->sql_get_shortest_flight_time( carrier ).
"Verifying result
cl_abap_unit_assert=>assert_equals(
act = result
exp = 65
msg = `Not the shortest flight`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD prepare_testdata_set_cds.
"Preparing and inserting test data
zdemo_abap_fli_tab = VALUE #(
( carrid = 'XX'
connid = 0407
fldate = '20231128'
price = '1102.77'
currency = 'JPY'
planetype = 'A380-800'
seatsmax = 475
seatsocc = 458
paymentsum = '563231.65'
seatsmax_b = 30
seatsocc_b = 27
seatsmax_f = 20
seatsocc_f = 20 )
( carrid = 'XX'
connid = 0407
fldate = '20231019'
price = '1102.77'
currency = 'JPY'
planetype = 'A380-800'
seatsmax = 475
seatsocc = 452
paymentsum = '553552.12'
seatsmax_b = 30
seatsocc_b = 28
seatsmax_f = 20
seatsocc_f = 19 )
( carrid = 'XX'
connid = 0408
fldate = '20231128'
price = '1102.77'
currency = 'JPY'
planetype = '747-400'
seatsmax = 385
seatsocc = 365
paymentsum = '470129.20'
seatsmax_b = 31
seatsocc_b = 28
seatsmax_f = 21
seatsocc_f = 20 )
( carrid = 'XX'
connid = 0408
fldate = '20230123'
price = '1102.77'
currency = 'JPY'
planetype = '747-400'
seatsmax = 385
seatsocc = 372
paymentsum = '487715.90'
seatsmax_b = 31
seatsocc_b = 31
seatsmax_f = 21
seatsocc_f = 20 ) ).
cds_env->insert_test_data( i_data = zdemo_abap_fli_tab ).
ENDMETHOD.
METHOD test_select_cds.
"--- 3) ---
"Preparing and inserting test data
prepare_testdata_set_cds( ).
"Calling method of the class under test
DATA carrier TYPE zdemo_abap_fli-carrid VALUE 'XX'.
DATA(act_result) = cut->cds_get_data_set( carrier ).
"Verifying result
cl_abap_unit_assert=>assert_equals(
act = act_result
exp = VALUE zdemo_abap_cds_ve_agg_exp( carrid = 'XX' currency = 'JPY' avg_seats_occ = '411.75'
avg_paysum = '518657.22' total_paysum = '2074628.87'
min_occ_seats = 365 max_occ_seats = 458
max_occ_seats_all = 458 cnt = 4 cnt_planetype = 2 )
msg = `The values do not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_eml_read_root.
"--- 4a) ---
"Mocking ABAP EML API (read request)
"Preparing test data (RAP BO instances)
DATA read_tab_ro_import TYPE TABLE FOR READ IMPORT zdemo_abap_rap_ro_m.
DATA read_tab_ro_result TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_m.
read_tab_ro_import = VALUE #( ( key_field = 2 ) ).
read_tab_ro_result = VALUE #( ( key_field = 2 field1 = 'uuu' field2 = 'vvv' field3 = 20 field4 = 200 ) ).
"Configuring input and output for the ABAP EML read request
"Creating input/output configuration builders
DATA(input_config_read) = cl_botd_mockemlapi_bldrfactory=>get_input_config_builder( )->for_read( ).
DATA(output_config_read) = cl_botd_mockemlapi_bldrfactory=>get_output_config_builder( )->for_read( ).
"Creating input for the entities (here, only one entity is included)
"Instead of the entity name, an alias name is also accepted.
DATA(eml_read_input) = input_config_read->build_entity_part( entity
)->set_instances_for_read( read_tab_ro_import ).
"Input configuration
DATA(input) = input_config_read->build_input_for_eml( )->add_entity_part( eml_read_input ).
"Output configuration
DATA(output) = output_config_read->build_output_for_eml( )->set_result_for_read( read_tab_ro_result ).
"Configuring the RAP BO test double
td_eml = eml_env->get_test_double( entity ).
td_eml->configure_call( )->for_read( )->when_input( input )->then_set_output( output ).
"Calling method (containing the ABAP EML read request) of the class under test
DATA(t_read_res_ro) = cut->eml_read_root( key = 2 ).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = lines( t_read_res_ro )
exp = 1
msg = `The number of lines does not match`
quit = if_abap_unit_constant=>quit-no ).
TYPES struc_read_ro TYPE STRUCTURE FOR READ RESULT zdemo_abap_rap_ro_m.
cl_abap_unit_assert=>assert_equals(
act = t_read_res_ro[ 1 ]
exp = VALUE struc_read_ro( key_field = 2 field1 = 'uuu' field2 = 'vvv' field3 = 20 field4 = 200 )
msg = `The values do not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_tdf_constr_inj_get_discnt.
"--- 1a) ---
"Method that demonstrates the ABAP OO Test Double Framework
"Injection mechanism: Constructor injection
"Creating a test double
DATA(test_double) = CAST zcl_demo_abap_unit_dataprov(
cl_abap_testdouble=>create( 'zcl_demo_abap_unit_dataprov' ) ).
"Configuring a method call
"It is used for the next method call (the actual method calling)
"In this example, the method only has a returning parameter. Check
"the class documentation for more static methods of the cl_abap_testdouble
"class, e.g. for configuring other parameters.
cl_abap_testdouble=>configure_call( test_double
)->returning( 5 ).
"Calling method of the external class
test_double->get_discount( ).
"Injecting the test double
"Here, the instance constructor is provided with the test double
"instance
cut = NEW #( test_double ).
"Calling method of the class under test
DATA(result) = cut->td_constr_inj_calc_discount( 500 ).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = result
exp = 475
msg = `The values do not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_tdf_param_inj_get_discnt.
"--- 1b) ---
"Method that demonstrates the ABAP OO Test Double Framework
"Injection mechanism: Parameter injection
"Creating a test double
DATA(test_double_param_inj) = CAST zcl_demo_abap_unit_dataprov(
cl_abap_testdouble=>create( 'zcl_demo_abap_unit_dataprov' ) ).
"Configuring a method call, ignoring importing parameters
cl_abap_testdouble=>configure_call( test_double_param_inj
)->ignore_parameter( 'DAY_VALUE'
)->ignore_parameter( 'TIME_VALUE'
)->returning( 40 ).
"Calling method of the external class
"Dummy values are provided for the non-optional importing
"parameters (the returning value is determined above)
test_double_param_inj->get_discount_value(
EXPORTING
day_value = 99
time_value = 99
).
"Calling method of the class under test
"In this case, the optional parameter is assigned the
"test double replacing the DOC
cut->td_param_inj_calc_discount(
EXPORTING
value = 100
date = '20241201' "Sunday
time = '100000'
data_prov = test_double_param_inj
IMPORTING
message = DATA(msg)
weekday = DATA(weekday)
RECEIVING
result = DATA(result)
).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = result
exp = 60
msg = `The values do not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_eml_rba.
"--- 4a) ---
"Method that demonstrates mocking an ABAP EML API (read-by-association request)
"Preparing test data (RAP BO instances)
DATA read_tab_ch_import TYPE TABLE FOR READ IMPORT zdemo_abap_rap_ro_m\_child.
DATA read_tab_ch_result TYPE TABLE FOR READ RESULT zdemo_abap_rap_ro_m\_child.
read_tab_ch_import = VALUE #( ( key_field = 2 ) ).
read_tab_ch_result = VALUE #( ( key_field = 2 key_ch = 22 field_ch1 = 'www' field_ch2 = 222 )
( key_field = 2 key_ch = 23 field_ch1 = 'xxx' field_ch2 = 223 ) ).
"Configuring input and output for the ABAP EML read-by-association request
DATA(input_config_rba) = cl_botd_mockemlapi_bldrfactory=>get_input_config_builder( )->for_read( ).
DATA(output_config_rba) = cl_botd_mockemlapi_bldrfactory=>get_output_config_builder( )->for_read( ).
"Creating input for the entities (here, only one entity is included)
DATA(eml_rba_input) = input_config_rba->build_entity_part( entity
)->set_instances_for_read_ba( read_tab_ch_import ).
"Input configuration
DATA(input_rba) = input_config_rba->build_input_for_eml( )->add_entity_part( eml_rba_input ).
"Output configuration
DATA(output_rba) = output_config_rba->build_output_for_eml(
)->set_result_for_read_ba(
source_entity_name = entity
assoc_name = '_CHILD'
result = read_tab_ch_result ).
"Configuring the RAP BO test double
td_eml = eml_env->get_test_double( entity ).
td_eml->configure_call( )->for_read( )->when_input( input_rba )->then_set_output( output_rba ).
"Calling method (containing the ABAP EML read-by-association request)
"of the class under test
DATA(t_read_res_ch) = cut->eml_rba( key = 2 ).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = lines( t_read_res_ch )
exp = 2
msg = `The number of lines does not match`
quit = if_abap_unit_constant=>quit-no ).
TYPES struc_read_ch TYPE STRUCTURE FOR READ RESULT zdemo_abap_rap_ro_m\_child.
cl_abap_unit_assert=>assert_equals(
act = t_read_res_ch[ 2 ]
exp = VALUE struc_read_ch( key_field = 2 key_ch = 23 field_ch1 = 'xxx' field_ch2 = 223 )
msg = `The values do not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_eml_modify_root.
"--- 4a) ---
"Method that demonstrates mocking an ABAP EML API (create request)
"Data object delcarations used in the test method
DATA create_tab_ro TYPE TABLE FOR CREATE zdemo_abap_rap_ro_m.
DATA mapped_resp TYPE RESPONSE FOR MAPPED EARLY zdemo_abap_rap_ro_m.
DATA failed_resp TYPE RESPONSE FOR FAILED EARLY zdemo_abap_rap_ro_m.
DATA reported_resp TYPE RESPONSE FOR REPORTED EARLY zdemo_abap_rap_ro_m.
"Preparing test data (RAP BO instances, response parameters)
create_tab_ro = VALUE #( ( %cid = `cid1` key_field = 3 field1 = 'aaa' field2 = 'bbb' field3 = 30 field4 = 300 )
( %cid = `cid2` key_field = 4 field1 = 'ccc' field2 = 'ddd' field3 = 40 field4 = 400 ) ).
mapped_resp-root = VALUE #( ( %cid = 'cid1' key_field = 3 ) ( %cid = 'cid2' key_field = 4 ) ).
failed_resp-root = VALUE #( ).
reported_resp-root = VALUE #( ).
"Configuring input and output for the ABAP EML create request
DATA(input_config_modify) = cl_botd_mockemlapi_bldrfactory=>get_input_config_builder( )->for_modify( ).
DATA(output_config_modify) = cl_botd_mockemlapi_bldrfactory=>get_output_config_builder( )->for_modify( ).
"Creating input for the entities (here, only one entity is included)
DATA(eml_modify_input) = input_config_modify->build_entity_part( entity
)->set_instances_for_create( create_tab_ro ).
"Input configuration
DATA(input) = input_config_modify->build_input_for_eml( )->add_entity_part( eml_modify_input ).
"Output configuration
DATA(output) = output_config_modify->build_output_for_eml( )->set_mapped( mapped_resp
)->set_failed( failed_resp )->set_reported( reported_resp ).
"Configuring the RAP BO test double
td_eml = eml_env->get_test_double( entity ).
td_eml->configure_call( )->for_modify( )->when_input( input )->then_set_output( output ).
"Calling method (containing the ABAP EML read request) of the class under test
cut->eml_modify_root(
EXPORTING
instances = VALUE #( ( %cid = `cid1` key_field = 3 field1 = 'aaa' field2 = 'bbb' field3 = 30 field4 = 300 )
( %cid = `cid2` key_field = 4 field1 = 'ccc' field2 = 'ddd' field3 = 40 field4 = 400 ) )
IMPORTING
mapped = DATA(m)
failed = DATA(f)
reported = DATA(r)
).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = lines( m-root )
exp = 2
msg = `The number of lines does not match`
quit = if_abap_unit_constant=>quit-no ).
cl_abap_unit_assert=>assert_equals(
act = lines( f-root )
exp = 0
msg = `The number of lines does not match`
quit = if_abap_unit_constant=>quit-no ).
cl_abap_unit_assert=>assert_equals(
act = lines( r-root )
exp = 0
msg = `The number of lines does not match`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_eml_read_root_buffer_td.
"--- 4b) ---
"Method that demonstrates transactional buffer test doubles
"Creating test double
"Note: You have more configuration options. Check, for example, the
"methods configure_additional_behavior, set_fields_handler methods, etc.
"as described in the documentation.
DATA(double) = buffer_env->get_test_double( 'zdemo_abap_rap_ro_u' ).
"Preparing test data
"Populating the transactional buffer with an ABAP EML create request
MODIFY ENTITIES OF zdemo_abap_rap_ro_u
ENTITY root
CREATE FIELDS ( key_field field1 field2 field3 field4 )
WITH VALUE #( ( %cid = `cid5` key_field = 5 field1 = 'eee' )
( %cid = `cid6` key_field = 6 field1 = 'fff' ) )
REPORTED DATA(reported)
FAILED DATA(failed)
MAPPED DATA(mapped).
"Calling method (containing an ABAP EML read request) of the class under test
cut->eml_read_root_buffer_td(
EXPORTING
key = 5
RECEIVING
tab_ro_u = DATA(read_result) ).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = read_result[ 1 ]-key_field
exp = 5
msg = `The value does not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
cl_abap_unit_assert=>assert_equals(
act = read_result[ 1 ]-field1
exp = 'eee'
msg = `The value does not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
"Another calling of the method of the class under test (non-existent instance
"in the transactional buffer test double)
cut->eml_read_root_buffer_td(
EXPORTING
key = 1
RECEIVING
tab_ro_u = DATA(read_result_f) ).
cl_abap_unit_assert=>assert_initial(
EXPORTING
act = read_result_f
msg = `An instance was found`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
METHOD test_cds_standalone.
"--- 3) ---
"Method that tests the implementation logic of a CDS view entity
"Unlike the 'test_select_cds' method, this method does not call a method
"in the class under test.
"Preparing test data
prepare_testdata_set_cds( ).
"Retrieving data from the CDS view entity (test data is used here)
SELECT * FROM zdemo_abap_cds_ve_agg_exp INTO TABLE @DATA(result).
"Verifying the result
cl_abap_unit_assert=>assert_equals(
act = result[ 1 ]-carrid
exp = 'XX'
msg = `The value does not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
cl_abap_unit_assert=>assert_equals(
act = result[ 1 ]-avg_seats_occ
exp = '411.75'
msg = `The value does not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
cl_abap_unit_assert=>assert_equals(
act = result[ 1 ]-max_occ_seats
exp = 458
msg = `The value does not match the expected result`
quit = if_abap_unit_constant=>quit-no ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<abapGit version="v1.0.0" serializer="LCL_OBJECT_CLAS" serializer_version="v1.0.0">
<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
<asx:values>
<VSEOCLASS>
<CLSNAME>ZTCL_DEMO_ABAP_UNIT_TDF_TESTCL</CLSNAME>
<LANGU>E</LANGU>
<DESCRIPT>ABAP cheat sheet: Creating Test Doubles Using ABAP Framework</DESCRIPT>
<STATE>1</STATE>
<CLSCCINCL>X</CLSCCINCL>
<FIXPT>X</FIXPT>
<UNICODE>X</UNICODE>
<WITH_UNIT_TESTS>X</WITH_UNIT_TESTS>
</VSEOCLASS>
</asx:values>
</asx:abap>
</abapGit>