Update
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@
|
|||||||
- [More (Special) Functions](#more-special-functions)
|
- [More (Special) Functions](#more-special-functions)
|
||||||
- [coalesce Function](#coalesce-function)
|
- [coalesce Function](#coalesce-function)
|
||||||
- [More Information](#more-information)
|
- [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.
|
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>
|
<br><br>
|
||||||
|
|
||||||
``` abap
|
``` abap
|
||||||
DATA(str) =`Pieces of cakes.`.
|
DATA(str) = `Pieces of cakes.`.
|
||||||
|
|
||||||
"---------------- find ----------------
|
"---------------- find ----------------
|
||||||
"The find function searches for the substring specified and returns the offset
|
"The find function searches for the substring specified and returns the offset
|
||||||
@@ -1690,13 +1691,6 @@ SELECT SINGLE
|
|||||||
source_unit = unit`MI`,
|
source_unit = unit`MI`,
|
||||||
target_unit = unit`KM` ) AS miles_to_km,
|
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
|
"Creating a unique UUID for each row
|
||||||
uuid( ) AS uuid
|
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)
|
- [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)
|
- [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)
|
||||||
|
|||||||
@@ -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)|
|
|[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)|
|
|[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)|
|
|[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)|
|
|[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)|
|
|[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)|
|
|[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)|
|
||||||
|
|||||||
1708
src/zcl_demo_abap_builtin_func.clas.abap
Normal file
1708
src/zcl_demo_abap_builtin_func.clas.abap
Normal file
File diff suppressed because it is too large
Load Diff
16
src/zcl_demo_abap_builtin_func.clas.xml
Normal file
16
src/zcl_demo_abap_builtin_func.clas.xml
Normal 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>
|
||||||
67
src/zcl_demo_abap_unit_dataprov.clas.abap
Normal file
67
src/zcl_demo_abap_unit_dataprov.clas.abap
Normal 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.
|
||||||
16
src/zcl_demo_abap_unit_dataprov.clas.xml
Normal file
16
src/zcl_demo_abap_unit_dataprov.clas.xml
Normal 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>
|
||||||
458
src/zcl_demo_abap_unit_tdf.clas.abap
Normal file
458
src/zcl_demo_abap_unit_tdf.clas.abap
Normal 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>"! @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.
|
||||||
16
src/zcl_demo_abap_unit_tdf.clas.xml
Normal file
16
src/zcl_demo_abap_unit_tdf.clas.xml
Normal 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>
|
||||||
@@ -47,6 +47,7 @@ CLASS zcl_demo_abap_xml_json DEFINITION
|
|||||||
deserialize_helper IMPORTING attr_string_a TYPE string
|
deserialize_helper IMPORTING attr_string_a TYPE string
|
||||||
attr_string_b TYPE string
|
attr_string_b TYPE string
|
||||||
attr_concat_string TYPE string.
|
attr_concat_string TYPE string.
|
||||||
|
|
||||||
ENDCLASS.
|
ENDCLASS.
|
||||||
|
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
|
|||||||
( ReleasedObjectName LIKE '%IXML%' OR ReleasedObjectName LIKE '%SXML%' )
|
( ReleasedObjectName LIKE '%IXML%' OR ReleasedObjectName LIKE '%SXML%' )
|
||||||
INTO TABLE @DATA(released_xml_libs).
|
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` ) ).
|
out->write( zcl_demo_abap_aux=>heading( `4) Parsing XML Data Using iXML` ) ).
|
||||||
"The example covers the following aspects:
|
"Notes on the example:
|
||||||
"- Parsing XML data to a DOM object in one go
|
"- XML data is transformed to an input steam object and imported to a DOM object in one go using a parser object
|
||||||
"- Directly reading nodes using various iXML methods
|
"- If the XML is successfully parsed, DOM object nodes are iterated and accessed
|
||||||
"- Directly reading nodes using element names
|
"- Elements of the XML data and their attributes are processed, read and output
|
||||||
"- Reading using iterators, i.e. going over the XML nodes one after another
|
"- The create_renderer method is used to render XML data into an output stream
|
||||||
"- 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
|
|
||||||
|
|
||||||
"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.
|
TRY.
|
||||||
DATA(some_xml) = cl_abap_conv_codepage=>create_out( )->convert(
|
DATA(some_xml) = cl_abap_conv_codepage=>create_out( )->convert(
|
||||||
`<hi>` &&
|
`<?xml version="1.0"?>` &&
|
||||||
` <word1>hallo</word1>` &&
|
`<node attr_a="123">` &&
|
||||||
` <word2>how</word2>` &&
|
` <subnode1>` &&
|
||||||
` <word3>are</word3>` &&
|
` <letter>A</letter>` &&
|
||||||
`</hi>` ).
|
` <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.
|
CATCH cx_sy_conversion_codepage.
|
||||||
ENDTRY.
|
ENDTRY.
|
||||||
|
|
||||||
@@ -237,77 +236,59 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
|
|||||||
document = document_pa
|
document = document_pa
|
||||||
stream_factory = stream_factory_pa ).
|
stream_factory = stream_factory_pa ).
|
||||||
|
|
||||||
"Parsing XML data to a DOM representation in one go. It is put in the memory.
|
IF parser_pa->parse( ) <> 0.
|
||||||
"Note: You can also parse sequentially, and not in one go.
|
"Processing errors
|
||||||
DATA(parsing_check) = parser_pa->parse( ).
|
DATA(error_pa_num) = parser_pa->num_errors( ).
|
||||||
IF parsing_check = 0. "Parsing was successful
|
|
||||||
|
|
||||||
"Directly reading nodes using various iXML methods
|
DO error_pa_num TIMES.
|
||||||
|
DATA(error_pa) = parser_pa->get_error(
|
||||||
"Accessing the root element of the DOM. It can be used as the initial node
|
index = sy-index - 1
|
||||||
"for accessing subnodes.
|
min_severity = 3
|
||||||
"You can check the content of the variables in the debugger.
|
).
|
||||||
"Note: Multiple methods are available to further process the nodes.
|
DATA(reason_pa) = error_pa->get_reason( ).
|
||||||
DATA(root_element) = document_pa->get_root_element( ).
|
out->write( |Error: { reason_pa }| ).
|
||||||
"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.
|
|
||||||
ENDDO.
|
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.
|
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.
|
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)` ) ).
|
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
|
"Creating an internal table for display purposes
|
||||||
DATA: BEGIN OF node_info,
|
DATA: BEGIN OF node_info,
|
||||||
node_type TYPE string,
|
node_type TYPE string,
|
||||||
prefix TYPE string,
|
name TYPE string,
|
||||||
name TYPE string,
|
value TYPE string,
|
||||||
nsuri TYPE string,
|
|
||||||
value_type TYPE string,
|
|
||||||
value TYPE string,
|
|
||||||
value_raw TYPE xstring,
|
|
||||||
END OF node_info,
|
END OF node_info,
|
||||||
nodes_tab LIKE TABLE 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.
|
"To iterate accros all nodes, you can call the NEXT_NODE method.
|
||||||
TRY.
|
TRY.
|
||||||
DO.
|
DO.
|
||||||
"Check out other available methods in ADT by placing the cursor behind ->
|
CLEAR node_info.
|
||||||
"and choosing CTRL + Space.
|
|
||||||
reader->next_node( ).
|
reader->next_node( ).
|
||||||
|
|
||||||
"When reaching the end of the XML data, the loop is exited.
|
"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.
|
"You can access the properties of the node directly.
|
||||||
"For display purposes, the property information is stored in an internal table.
|
"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 example here just uses simple demo JSON data. Not all properties are
|
||||||
"the values for these are initial.
|
"retrieved and displayed.
|
||||||
|
|
||||||
"Node type, see the interface if_sxml_node
|
"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`
|
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_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_element_close THEN `CO_NT_ELEMENT_CLOSE`
|
||||||
WHEN if_sxml_node=>co_nt_value THEN `CO_NT_VALUE`
|
WHEN if_sxml_node=>co_nt_value THEN `CO_NT_VALUE`
|
||||||
WHEN if_sxml_node=>co_nt_attribute THEN `CO_NT_ATTRIBUTE`
|
WHEN if_sxml_node=>co_nt_attribute THEN `CO_NT_ATTRIBUTE`
|
||||||
ELSE `Error` ).
|
ELSE `Error` ).
|
||||||
|
"Name of the element
|
||||||
DATA(prefix) = reader->prefix. "Namespace prefix
|
node_info-name = reader->name.
|
||||||
DATA(name) = reader->name. "Name of the element
|
"Character-like value (if it is textual data)
|
||||||
DATA(nsuri) = reader->nsuri. "Namespace URI
|
node_info-value = COND #( WHEN reader->node_type = if_sxml_node=>co_nt_value THEN reader->value ).
|
||||||
|
APPEND node_info TO nodes_tab.
|
||||||
"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.
|
|
||||||
|
|
||||||
"Once the method is called, you can directly access the attributes of the reader with the required
|
"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
|
"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.
|
IF reader->node_type <> if_sxml_node=>co_nt_attribute.
|
||||||
EXIT.
|
EXIT.
|
||||||
ENDIF.
|
ENDIF.
|
||||||
APPEND VALUE #( node_type = `attribute`
|
APPEND VALUE #( node_type = `CO_NT_ATTRIBUTE`
|
||||||
prefix = reader->prefix
|
|
||||||
name = reader->name
|
name = reader->name
|
||||||
nsuri = reader->nsuri
|
value = reader->value ) TO nodes_tab.
|
||||||
value = reader->value
|
|
||||||
value_raw = reader->value_raw ) TO nodes_tab.
|
|
||||||
ENDDO.
|
ENDDO.
|
||||||
ENDIF.
|
ENDIF.
|
||||||
ENDDO.
|
ENDDO.
|
||||||
|
|
||||||
|
out->write( nodes_tab ).
|
||||||
CATCH cx_sxml_state_error INTO DATA(error_parse_token).
|
CATCH cx_sxml_state_error INTO DATA(error_parse_token).
|
||||||
out->write( error_parse_token->get_text( ) ).
|
out->write( error_parse_token->get_text( ) ).
|
||||||
ENDTRY.
|
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)` ) ).
|
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 ).
|
DATA(open_element) = CAST if_sxml_open_element( node_oo ).
|
||||||
|
|
||||||
APPEND VALUE #( node_type = `open element`
|
APPEND VALUE #( node_type = `open element`
|
||||||
prefix = open_element->prefix
|
|
||||||
name = open_element->qname-name
|
name = open_element->qname-name
|
||||||
nsuri = open_element->qname-namespace
|
|
||||||
) TO nodes_tab.
|
) TO nodes_tab.
|
||||||
|
|
||||||
DATA(attributes) = open_element->get_attributes( ).
|
DATA(attributes) = open_element->get_attributes( ).
|
||||||
|
|
||||||
LOOP AT attributes INTO DATA(attribute).
|
LOOP AT attributes INTO DATA(attribute).
|
||||||
APPEND VALUE #( node_type = `attribute`
|
APPEND VALUE #( node_type = `attribute`
|
||||||
prefix = open_element->prefix
|
|
||||||
name = open_element->qname-name
|
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 = 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.
|
) TO nodes_tab.
|
||||||
ENDLOOP.
|
ENDLOOP.
|
||||||
|
|
||||||
@@ -572,9 +521,7 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
|
|||||||
DATA(close_element) = CAST if_sxml_close_element( node_oo ).
|
DATA(close_element) = CAST if_sxml_close_element( node_oo ).
|
||||||
|
|
||||||
APPEND VALUE #( node_type = `close element`
|
APPEND VALUE #( node_type = `close element`
|
||||||
prefix = open_element->prefix
|
|
||||||
name = open_element->qname-name
|
name = open_element->qname-name
|
||||||
nsuri = open_element->qname-namespace
|
|
||||||
) TO nodes_tab.
|
) TO nodes_tab.
|
||||||
|
|
||||||
WHEN if_sxml_node=>co_nt_value.
|
WHEN if_sxml_node=>co_nt_value.
|
||||||
@@ -582,7 +529,6 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
|
|||||||
|
|
||||||
APPEND VALUE #( node_type = `value`
|
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 = 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.
|
) TO nodes_tab.
|
||||||
|
|
||||||
WHEN OTHERS.
|
WHEN OTHERS.
|
||||||
@@ -593,7 +539,6 @@ CLASS zcl_demo_abap_xml_json IMPLEMENTATION.
|
|||||||
out->write( error_parse_oo->get_text( ) ).
|
out->write( error_parse_oo->get_text( ) ).
|
||||||
ENDTRY.
|
ENDTRY.
|
||||||
|
|
||||||
out->write( `Node properties:` ).
|
|
||||||
out->write( nodes_tab ).
|
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.
|
"Note: When the identity transformation ID is used, the format is asJSON.
|
||||||
|
|
||||||
"Elementary type
|
"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
|
"Note: Unlike above, the following snippets do not work with asJSON as intermediate
|
||||||
"format.
|
"format.
|
||||||
|
|
||||||
DATA: BEGIN OF carrier_struc,
|
DATA: BEGIN OF carrier_struc,
|
||||||
carrier_id TYPE c length 3,
|
carrier_id TYPE c LENGTH 3,
|
||||||
connection_id TYPE n length 4,
|
connection_id TYPE n LENGTH 4,
|
||||||
city_from TYPE c length 20,
|
city_from TYPE c LENGTH 20,
|
||||||
city_to TYPE c length 20,
|
city_to TYPE c LENGTH 20,
|
||||||
END OF carrier_struc.
|
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' ).
|
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' )
|
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 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
|
"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
|
"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.
|
IF xml_oref_a = xstr_decomp.
|
||||||
out->write( `The decompressed binary data object has the same value as the original binary data object.` ).
|
out->write( `The decompressed binary data object has the same value as the original binary data object.` ).
|
||||||
ENDIF.
|
ENDIF.
|
||||||
|
|
||||||
ENDMETHOD.
|
ENDMETHOD.
|
||||||
METHOD format.
|
METHOD format.
|
||||||
TRY.
|
TRY.
|
||||||
|
|||||||
25
src/ztcl_demo_abap_unit_tdf_testcl.clas.abap
Normal file
25
src/ztcl_demo_abap_unit_tdf_testcl.clas.abap
Normal 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>"! @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.
|
||||||
551
src/ztcl_demo_abap_unit_tdf_testcl.clas.testclasses.abap
Normal file
551
src/ztcl_demo_abap_unit_tdf_testcl.clas.testclasses.abap
Normal 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.
|
||||||
17
src/ztcl_demo_abap_unit_tdf_testcl.clas.xml
Normal file
17
src/ztcl_demo_abap_unit_tdf_testcl.clas.xml
Normal 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>
|
||||||
Reference in New Issue
Block a user