From c8388b37d8a9be20a9b71b58fe0447e780d5dcf2 Mon Sep 17 00:00:00 2001
From: danrega <16720986+danrega@users.noreply.github.com>
Date: Mon, 23 Dec 2024 13:00:14 +0100
Subject: [PATCH] Update
---
14_ABAP_Unit_Tests.md | 1121 +----------
24_Builtin_Functions.md | 21 +-
README.md | 2 +-
src/zcl_demo_abap_builtin_func.clas.abap | 1708 +++++++++++++++++
src/zcl_demo_abap_builtin_func.clas.xml | 16 +
src/zcl_demo_abap_unit_dataprov.clas.abap | 67 +
src/zcl_demo_abap_unit_dataprov.clas.xml | 16 +
src/zcl_demo_abap_unit_tdf.clas.abap | 458 +++++
src/zcl_demo_abap_unit_tdf.clas.xml | 16 +
src/zcl_demo_abap_xml_json.clas.abap | 374 ++--
src/ztcl_demo_abap_unit_tdf_testcl.clas.abap | 25 +
...abap_unit_tdf_testcl.clas.testclasses.abap | 551 ++++++
src/ztcl_demo_abap_unit_tdf_testcl.clas.xml | 17 +
13 files changed, 3126 insertions(+), 1266 deletions(-)
create mode 100644 src/zcl_demo_abap_builtin_func.clas.abap
create mode 100644 src/zcl_demo_abap_builtin_func.clas.xml
create mode 100644 src/zcl_demo_abap_unit_dataprov.clas.abap
create mode 100644 src/zcl_demo_abap_unit_dataprov.clas.xml
create mode 100644 src/zcl_demo_abap_unit_tdf.clas.abap
create mode 100644 src/zcl_demo_abap_unit_tdf.clas.xml
create mode 100644 src/ztcl_demo_abap_unit_tdf_testcl.clas.abap
create mode 100644 src/ztcl_demo_abap_unit_tdf_testcl.clas.testclasses.abap
create mode 100644 src/ztcl_demo_abap_unit_tdf_testcl.clas.xml
diff --git a/14_ABAP_Unit_Tests.md b/14_ABAP_Unit_Tests.md
index 0b5ba19..4c12f62 100644
--- a/14_ABAP_Unit_Tests.md
+++ b/14_ABAP_Unit_Tests.md
@@ -15,10 +15,9 @@
- [Injecting Test Doubles](#injecting-test-doubles)
- [Test Seams](#test-seams)
- [Creating Test Doubles Using ABAP Frameworks](#creating-test-doubles-using-abap-frameworks)
- - [Example: Creating Test Doubles Using ABAP Frameworks](#example-creating-test-doubles-using-abap-frameworks)
- [Running and Evaluating ABAP Unit Tests](#running-and-evaluating-abap-unit-tests)
- [More Information](#more-information)
- - [Executable Example](#executable-example)
+ - [Executable Examples](#executable-examples)
This cheat sheet contains basic information about [unit testing](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenunit_test_glosry.htm) in ABAP.
@@ -60,7 +59,8 @@ This cheat sheet contains basic information about [unit testing](https://help.sa
Before we look at test doubles and injections, we will look at the creation of the test classes and methods to get an idea of the code skeletons (in which test doubles and injections can be implemented).
Test classes ...
-- are special [local](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlocal_class_glosry.htm) or [global classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenglobal_class_glosry.htm) in which tests for ABAP Unit are implemented in the form of test methods. Note: In this cheat sheet, the focus is on local test classes only.
+- are special [local](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenlocal_class_glosry.htm) or [global classes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenglobal_class_glosry.htm) in which tests for ABAP Unit are implemented in the form of test methods.
+ - You can define a test relation between a test class or a test method and another repository object using the ABAP Doc comment `"! @testing ...`. This is demonstrated by an [executable example](#executable-examples) class.
- are created in [class pools](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenclass_pool_glosry.htm) in special [test includes](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abentest_include_glosry.htm). See the *Test Classes* tab in the ADT.
- can only be used as part of test runs.
- are not generated in production systems, i.e. the source code of a test class is not part of the production code of its program.
@@ -562,1101 +562,7 @@ Several frameworks are available and demonstrated in the example classes below.
-### Example: Creating Test Doubles Using ABAP Frameworks
-The example classes below illustrate how to create and inject test doubles using the mentioned ABAP frameworks.
-
-To run the example, follow these steps:
-- First, import the ABAP cheat sheet repository. The example relies on some of its repository objects.
-- Next, create two classes: `ZCL_DEMO_ABAP_UNIT_TDF` (the class under test) and `ZCL_DEMO_ABAP_UNIT_DATAPROV` (the DOC containing methods called by the first class). Copy and paste the provided code into these classes. Note that `ZCL_DEMO_ABAP_UNIT_TDF` includes a test class in the test include (*Test Classes* tab in ADT).
-- The example is set up to display content in the ADT console when you run the class with *F9*. This is solely to demonstrate the effects of statements and method calls. The main focus is on test methods that explore test double creation using the frameworks. To launch the tests in the class, use *Ctrl/Cmd + Shift + F10*.
-
-> **💡 Note**
-> - The example classes are intentionally simplified and nonsemantic, designed to highlight basic unit tests and explore framework classes and methods.
-> - They are not meant to serve as a model for proper unit test design. Always devise your own solutions for each unique case.
-> - Refer to the code comments, SAP Help Portal and class documentation for additional information.
-
-
-Expand the following collapsible section for example code.
-
-🟢 Click to expand for example code
-
-
-
-**Class 1 `ZCL_DEMO_ABAP_UNIT_TDF` (the class under test)**
-
-1a) Insert the following code in the `Global Class` tab
-
-
-```abap
-CLASS zcl_demo_abap_unit_tdf DEFINITION
- PUBLIC
- FINAL
- CREATE PUBLIC .
-
- PUBLIC SECTION.
-
-* -------------------------- NOTE ----------------------------------
-* - This is an example class that demonstrates managing dependencies
-* (dependent-on-components, DOC) with ABAP Unit.
-* - Topics covered:
-* - ABAP OO Test Double Framework (test doubles injected using
-* constructor and parameter injection)
-* - ABAP SQL Test Double Framework
-* - ABAP CDS Test Double Framework
-* - Managing dependencies on RAP business objects (mocking ABAP EML
-* APIs, transactional buffer test doubles)
-*
-* ----------------------- RUN ABAP UNIT TEST---------------------------
-* - Open the class with the ABAP development tools for Eclipse (ADT).
-* - Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class.
-* You can also right-click in the class and choose Run as -> ABAP Unit
-* Test.
-* - The results of a test run are displayed in the ABAP Unit tab in ADT
-* and can be evaluated.
-*
-* ----------------------- RUN CLASS -----------------------------
-* - Open the class with the ABAP development tools for Eclipse (ADT).
-* - Choose F9 to run the class and check the console output.
-
- 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.
- "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.
-```
-
-1b) Insert the following code in the `Test Classes` tab (it is a local test class of `ZCL_DEMO_ABAP_UNIT_TDF`)
-
-```abap
-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.
-```
-
-**Class 2 `ZCL_DEMO_ABAP_UNIT_DATAPROV` (class representing a DOC)**
-
-Insert the following code in the `Global Class` tab
-
-```abap
-CLASS zcl_demo_abap_unit_dataprov DEFINITION
- PUBLIC
- CREATE PUBLIC .
-
- PUBLIC SECTION.
-
-* -------------------------- NOTE ----------------------------------
-* This is an example class that represents a dependent-on-component
-* (DOC). Methods of this class are called in another class. There,
-* the DOCs are replaced by test doubles when running ABAP Unit tests.
-
- 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.
-```
-
-
-
Built-in Functions
ABAP cheat sheet example class
The example class demonstrates built-in functions in ABAP.
+"! Choose F9 in ADT to run the class.
Find information on getting started with the example class and the disclaimer in +"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.
+CLASS zcl_demo_abap_builtin_func DEFINITION + PUBLIC + FINAL + CREATE PUBLIC . + + PUBLIC SECTION. + INTERFACES if_oo_adt_classrun. + CLASS-METHODS class_constructor. + PROTECTED SECTION. + PRIVATE SECTION. +ENDCLASS. + +CLASS zcl_demo_abap_builtin_func IMPLEMENTATION. + METHOD if_oo_adt_classrun~main. + + out->write( |ABAP cheat sheet example: Built-in Functions\n\n| ). + out->write( `1) Logical Functions` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& boolc +*&---------------------------------------------------------------------* + + "boolc returns an X or a blank of type string + DATA(int) = 0. + + DATA(boolc1) = CONV abap_bool( boolc( int IS INITIAL ) ). + out->write( data = boolc1 name = `boolc1` ). + out->write( |\n| ). + + DATA(boolc2) = |#{ boolc( int IS INITIAL ) }#|. + out->write( data = boolc2 name = `boolc2` ). + out->write( |\n| ). + + DATA(boolc3) = |#{ boolc( int IS NOT INITIAL ) }#|. + out->write( data = boolc3 name = `boolc3` ). + out->write( |\n| ). + + "Using the translate function to return a value other than X/blank + DATA(boolc4) = translate( val = boolc( int BETWEEN -3 AND 3 ) from = `X` to = `1` ). + out->write( data = boolc4 name = `boolc4` ). + out->write( |\n| ). + + DATA(boolc5) = translate( val = boolc( int <> 0 ) from = ` ` to = `0` ). + out->write( data = boolc5 name = `boolc5` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& xsdbool +*&---------------------------------------------------------------------* + + + DATA(xsdb1) = xsdbool( 3 > 1 ). + out->write( data = xsdb1 name = `xsdb1` ). + out->write( |\n| ). + + DATA(xsdb2) = |#{ xsdbool( 1 = 1 ) }#|. + out->write( data = xsdb2 name = `xsdb2` ). + out->write( |\n| ). + + DATA(xsdb3) = |#{ xsdbool( 1 <> 1 ) }#|. + out->write( data = xsdb3 name = `xsdb3` ). + out->write( |\n| ). + + "Comparison with boolc + IF boolc( 1 = 0 ) = xsdbool( 1 = 0 ). + DATA(res) = `equal`. + ELSE. + res = `not equal`. + ENDIF. + out->write( data = res name = `res` ). + out->write( |\n| ). + + "Using xsdbool instead of, for example, an IF control + "structure or an expression with the COND operator + DATA(xsdb4) = xsdbool( -1 < 1 ). + out->write( data = xsdb4 name = `xsdb4` ). + out->write( |\n| ). + + DATA truth_value1 TYPE abap_bool. + IF -1 < 1. + truth_value1 = abap_true. + ELSE. + truth_value1 = abap_false. + ENDIF. + out->write( data = truth_value1 name = `truth_value1` ). + out->write( |\n| ). + + DATA(truth_value2) = COND #( WHEN -1 < 1 THEN abap_true ELSE abap_false ). + out->write( data = truth_value2 name = `truth_value2` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& contains, contains_any_of, contains_any_not_of +*&---------------------------------------------------------------------* + + "-------------------- contains -------------------- + "Specifying the minimum mandatory parameters + "Unlike most of the following examples, this one uses an IF control structure to + "visualize the truth value. + DATA cont1 TYPE abap_bool. + + IF contains( val = `abdefghijklmn` sub = `ghi` ). + cont1 = abap_true. + ELSE. + cont1 = abap_false. + ENDIF. + out->write( data = cont1 name = `cont1` ). + out->write( |\n| ). + + "case (abap_true is the default) + + DATA(cont2) = xsdbool( contains( val = `ABCDE` start = `ab` case = abap_true ) ). + out->write( data = cont2 name = `cont2` ). + out->write( |\n| ). + + DATA(cont3) = xsdbool( contains( val = `ABCDE` start = `ab` case = abap_false ) ). + out->write( data = cont3 name = `cont3` ). + out->write( |\n| ). + + "end + DATA(cont4) = xsdbool( contains( val = `UVWXYZ` end = `xyz` case = abap_false ) ). + out->write( data = cont4 name = `cont4` ). + out->write( |\n| ). + + "start + DATA(cont5) = xsdbool( contains( val = `123` start = `2` ) ). + out->write( data = cont5 name = `cont5` ). + out->write( |\n| ). + + "off/len can also be specified individually + "Not specifying off means 0 by default + DATA(cont6) = xsdbool( contains( val = `##ab## ##cd##` sub = `cd` len = 5 ) ). + out->write( data = cont6 name = `cont6` ). + out->write( |\n| ). + + DATA(cont7) = xsdbool( contains( val = `##ab## ##cd##` sub = `cd` off = 7 len = 5 ) ). + out->write( data = cont7 name = `cont7` ). + out->write( |\n| ). + + "occ: False if there are more occurrences than specified for occ; i.e. in the following + "example, specifying the values 1, 2, 3 returns true + "abap_true is returned for the first 3 loop passes, abap_false for the fourth + DO 4 TIMES. + DATA(cont8) = xsdbool( contains( val = `ab#ab#ab#cd#ef#gh` sub = `ab` occ = sy-index ) ). + out->write( data = cont8 name = `cont8` ). + out->write( |\n| ). + ENDDO. + + "pcre + "In the example, a blank is searched. + DATA(cont9) = xsdbool( contains( val = `Hallo world` pcre = `\s` ) ). + out->write( data = cont9 name = `cont9` ). + out->write( |\n| ). + + "-------------------- contains_any_of -------------------- + DATA(cont10) = xsdbool( contains_any_of( val = `abcdefg` sub = `xyza` ) ). + out->write( data = cont10 name = `cont10` ). + out->write( |\n| ). + + DATA(cont11) = xsdbool( contains_any_of( val = `abcdefg` sub = `xyz` ) ). + out->write( data = cont11 name = `cont11` ). + out->write( |\n| ). + + DATA(hi) = `1hallo`. + DATA(abc) = `abcdefghijklmnopqrstuvwxyz`. + DATA(cont12) = xsdbool( contains_any_of( val = hi start = abc ) ). + out->write( data = cont12 name = `cont12` ). + out->write( |\n| ). + + DATA(cont13) = xsdbool( contains_any_of( val = hi end = abc ) ). + out->write( data = cont13 name = `cont13` ). + out->write( |\n| ). + + "-------------------- contains_any_not_of -------------------- + DATA(cont14) = xsdbool( contains_any_not_of( val = hi start = abc ) ). + out->write( data = cont14 name = `cont14` ). + out->write( |\n| ). + + DATA(cont15) = xsdbool( contains_any_not_of( val = hi end = abc ) ). + out->write( data = cont15 name = `cont15` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& matches +*&---------------------------------------------------------------------* + + "Checking validity of an email address + "abap_true + DATA(matches) = xsdbool( matches( val = `jon.doe@email.com` + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ) ). + out->write( data = matches name = `matches` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& line_exists +*&---------------------------------------------------------------------* + + TYPES: BEGIN OF s, + comp1 TYPE i, + comp2 TYPE c LENGTH 3, + END OF s. + DATA itab TYPE TABLE OF s WITH EMPTY KEY. + itab = VALUE #( ( comp1 = 1 comp2 = 'aaa' ) ( comp1 = 2 comp2 = 'bbb' ) ( comp1 = 3 comp2 = 'ccc' ) ). + DATA(str_tab) = VALUE string_table( ( `abc` ) ( `def` ) ( `ghi` ) ). + + + DATA(line_exists1) = xsdbool( line_exists( itab[ 1 ] ) ). + out->write( data = line_exists1 name = `line_exists1` ). + out->write( |\n| ). + + + DATA(line_exists2) = xsdbool( line_exists( itab[ 4 ] ) ). + out->write( data = line_exists2 name = `line_exists2` ). + out->write( |\n| ). + + + DATA(line_exists3) = xsdbool( line_exists( itab[ comp1 = 2 ] ) ). + out->write( data = line_exists3 name = `line_exists3` ). + out->write( |\n| ). + + + DATA(line_exists4) = xsdbool( line_exists( str_tab[ 2 ] ) ). + out->write( data = line_exists4 name = `line_exists4` ). + out->write( |\n| ). + + + DATA(line_exists5) = xsdbool( line_exists( str_tab[ table_line = `xxx` ] ) ). + out->write( data = line_exists5 name = `line_exists5` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `2) Numeric Functions` ) ). + +*&---------------------------------------------------------------------* +*& abs, sign, ceil, floor, trunc, frac, ipow +*&---------------------------------------------------------------------* + + "----------- abs: Returning the absolute value ----------- + + DATA(abs1) = abs( CONV decfloat34( '-4.756' ) ). + out->write( data = abs1 name = `abs1` ). + out->write( |\n| ). + + DATA(abs2) = abs( -4 ). + out->write( data = abs2 name = `abs2` ). + out->write( |\n| ). + + "----------- sign: Evaluating the sign ----------- + DATA(sign1) = sign( -789 ). + out->write( data = sign1 name = `sign1` ). + out->write( |\n| ). + + DATA(sign2) = sign( 5 - 5 ). + out->write( data = sign2 name = `sign2` ). + out->write( |\n| ). + + DATA(sign3) = sign( -5 * -5 ). + out->write( data = sign3 name = `sign3` ). + out->write( |\n| ). + + "----- ceil: smallest integer not less than the value specified ----- + + DATA(ceil1) = ceil( CONV decfloat34( '4.999' ) ). + out->write( data = ceil1 name = `ceil1` ). + out->write( |\n| ). + + DATA(ceil2) = ceil( CONV decfloat34( '4.001' ) ). + out->write( data = ceil2 name = `ceil2` ). + out->write( |\n| ). + + DATA(ceil3) = ceil( CONV decfloat34( '-4.999' ) ). + out->write( data = ceil3 name = `ceil3` ). + out->write( |\n| ). + + DATA(ceil4) = ceil( CONV decfloat34( '-4.001' ) ). + out->write( data = ceil4 name = `ceil4` ). + out->write( |\n| ). + + "----- floor: largest integer not less than the value specified ----- + + DATA(floor1) = floor( CONV decfloat34( '4.999' ) ). + out->write( data = floor1 name = `floor1` ). + out->write( |\n| ). + + DATA(floor2) = floor( CONV decfloat34( '4.001' ) ). + out->write( data = floor2 name = `floor2` ). + out->write( |\n| ). + + DATA(floor3) = floor( CONV decfloat34( '-4.999' ) ). + out->write( data = floor3 name = `floor3` ). + out->write( |\n| ). + + DATA(floor4) = floor( CONV decfloat34( '-4.001' ) ). + out->write( data = floor4 name = `floor4` ). + out->write( |\n| ). + + "------------- trunc: integer part ------------- + + DATA(trunc1) = trunc( CONV decfloat34( '4.999' ) ). + out->write( data = trunc1 name = `trunc1` ). + out->write( |\n| ). + + DATA(trunc2) = trunc( CONV decfloat34( '4.001' ) ). + out->write( data = trunc2 name = `trunc2` ). + out->write( |\n| ). + + DATA(trunc3) = trunc( CONV decfloat34( '-4.999' ) ). + out->write( data = trunc3 name = `trunc3` ). + out->write( |\n| ). + + DATA(trunc4) = trunc( CONV decfloat34( '-4.001' ) ). + out->write( data = trunc4 name = `trunc4` ). + out->write( |\n| ). + + "------------- frac: decimal places ------------- + + DATA(frac1) = frac( CONV decfloat34( '4.999' ) ). + out->write( data = frac1 name = `frac1` ). + out->write( |\n| ). + + DATA(frac2) = frac( CONV decfloat34( '4.001' ) ). + out->write( data = frac2 name = `frac2` ). + out->write( |\n| ). + + DATA(frac3) = frac( CONV decfloat34( '-4.999' ) ). + out->write( data = frac3 name = `frac3` ). + out->write( |\n| ). + + DATA(frac4) = frac( CONV decfloat34( '-4.001' ) ). + out->write( data = frac4 name = `frac4` ). + out->write( |\n| ). + + "------------- ipow: Calculalting the power ------------- + + DATA(ipow1) = ipow( base = 2 exp = 3 ). + out->write( data = ipow1 name = `ipow1` ). + out->write( |\n| ). + + DATA(ipow2) = ipow( base = 10 exp = 0 ). + out->write( data = ipow2 name = `ipow2` ). + out->write( |\n| ). + + "Exception is raised + TRY. + DATA(ipow3) = ipow( base = 10 exp = 100 ). + CATCH cx_sy_arithmetic_overflow INTO DATA(error). + out->write( error->get_text( ) ). + ENDTRY. + + +*&---------------------------------------------------------------------* +*& nmin, nmax +*&---------------------------------------------------------------------* + + "A minimum of two, and a maximum of 9 arguments can be specified. + "Numeric data objects and numeric expressions are possible + + DATA(nmin) = nmin( val1 = CONV decfloat34( '1.34' ) + val2 = CONV decfloat34( '56.7' ) + val3 = CONV decfloat34( '890.123' ) + val4 = CONV decfloat34( '0.999' ) ). + out->write( data = nmin name = `nmin` ). + out->write( |\n| ). + + DATA(nmax) = nmax( val1 = CONV decfloat34( '1.34' ) + val2 = CONV decfloat34( '56.7' ) + val3 = CONV decfloat34( '890.123' ) + val4 = CONV decfloat34( '0.999' ) ). + out->write( data = nmax name = `nmax` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& acos, asin, atan, cos, sin, tan, cosh, sinh, tanh, exp, log, log10, sqrt +*&---------------------------------------------------------------------* + + "Calculating the square root + + DATA(sqrt1) = sqrt( CONV decfloat34( '9' ) ). + out->write( data = sqrt1 name = `sqrt1` ). + out->write( |\n| ). + + DATA(sqrt2) = sqrt( CONV decfloat34( '40.96' ) ). + out->write( data = sqrt2 name = `sqrt2` ). + out->write( |\n| ). + + "Calculating the logarithm to base 10 + + DATA(log10) = log10( CONV decfloat34( '1000' ) ). + out->write( data = log10 name = `log10` ). + out->write( |\n| ). + + DATA(sine) = sin( '30' ). + out->write( data = sine name = `sine` ). + out->write( |\n| ). + + DATA(cosine) = cos( '45' ). + out->write( data = cosine name = `cosine` ). + out->write( |\n| ). + + DATA(tangent) = tan( '90' ). + out->write( data = tangent name = `tangent` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& round, rescale +*&---------------------------------------------------------------------* + + "Rounding to decimal places + + DATA(round1) = round( val = CONV decfloat34( '1.2374' ) dec = 2 ). + out->write( data = round1 name = `round1` ). + out->write( |\n| ). + + DATA(round2) = round( val = CONV decfloat34( '1.2374' ) dec = 3 ). + out->write( data = round2 name = `round2` ). + out->write( |\n| ). + + "Rounding to precision + + DATA(round3) = round( val = CONV decfloat34( '1234567890123' ) prec = 10 ). + out->write( data = round3 name = `round3` ). + out->write( |\n| ). + + DATA(round4) = round( val = CONV decfloat34( '1234' ) prec = 3 ). + out->write( data = round4 name = `round4` ). + out->write( |\n| ). + + "Rescaling function + "Similar to the round function, the dec (for scaling) or prec (for precision) + "parameters must be specified. The input is rounded if required. + + DATA(rescale1) = rescale( val = CONV decfloat34( '1234.56789' ) dec = 0 ). + out->write( data = rescale1 name = `rescale1` ). + out->write( |\n| ). + + DATA(rescale2) = rescale( val = CONV decfloat34( '1234.56789' ) dec = 1 ). + out->write( data = rescale2 name = `rescale2` ). + out->write( |\n| ). + + DATA(rescale3) = rescale( val = CONV decfloat34( '1234.56789' ) prec = 3 ). + out->write( data = rescale3 name = `rescale3` ). + out->write( |\n| ). + + DATA(rescale4) = rescale( val = CONV decfloat34( '1234.56789' ) prec = 10 ). + out->write( data = rescale4 name = `rescale4` ). + out->write( |\n| ). + +********************************************************************** + + out->write( zcl_demo_abap_aux=>heading( `3) String Functions` ) ). + +*&---------------------------------------------------------------------* +*& numofchar, strlen, xstrlen +*&---------------------------------------------------------------------* + + "numofchar: Trailing blanks are not counted in both strings of fixed and variable length + "strlen: Trailing blanks are not counted in strings of fixed length; in strings of + " variable length, they are counted + + DATA(numofchar1) = numofchar( 'abc ' ). + out->write( data = numofchar1 name = `numofchar1` ). + out->write( |\n| ). + + DATA(numofchar2) = numofchar( `abc ` ). + out->write( data = numofchar2 name = `numofchar2` ). + out->write( |\n| ). + + + DATA(strlen1) = strlen( 'abc ' ). + out->write( data = strlen1 name = `strlen1` ). + out->write( |\n| ). + + + DATA(strlen2) = strlen( `abc ` ). + out->write( data = strlen2 name = `strlen2` ). + out->write( |\n| ). + + "xstrlen for type xstring + DATA(xstr) = CONV xstring( `480065006C006C006F00200077006F0072006C0064002100` ). + + DATA(len_xstr) = xstrlen( xstr ). + out->write( data = len_xstr name = `len_xstr` ). + out->write( |\n| ). + + "xstring -> string + DATA(conv_str) = cl_abap_conv_codepage=>create_in( )->convert( xstr ). + +*&---------------------------------------------------------------------* +*& cmin, cmax +*&---------------------------------------------------------------------* + + DATA(cmin) = cmin( val1 = `zzzzzzz` + val2 = `zzazzzzzzzz` "smallest argument + val3 = `zzzzabc` ). + + out->write( data = cmin name = `cmin` ). + out->write( |\n| ). + + DATA(cmax) = cmax( val1 = `abcdef` "biggest argument + val2 = `aaghij` + val3 = `aaaaklmn` + val4 = `aaaaaaopqrs` + val5 = `aaaaaaaaaatuvwxy` + val6 = `aaaaaaaaaaaaaz` ). + out->write( data = cmax name = `cmax` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& find, find_end, find_any_of, find_any_not_of +*&---------------------------------------------------------------------* + + DATA(str) = `Pieces of cakes.`. + + "---------------- find ---------------- + "The find function searches for the substring specified and returns the offset + + DATA(find1) = find( val = str sub = `of` ). + out->write( data = find1 name = `find1` ). + out->write( |\n| ). + + DATA(find2) = find( val = str sub = `x` ). + out->write( data = find2 name = `find2` ). + out->write( |\n| ). + + "case + DATA(find3) = find( val = str sub = `p` case = abap_false ). + out->write( data = find3 name = `find3` ). + out->write( |\n| ). + + "off/len + DATA(find4) = find( val = str sub = `ca` off = 4 len = 5 ). + out->write( data = find4 name = `find4` ). + out->write( |\n| ). + + DATA(find5) = find( val = str sub = `ca` off = 4 len = 10 ). + out->write( data = find5 name = `find5` ). + out->write( |\n| ). + + "occ + DATA(find6) = find( val = str sub = `es` occ = 1 ). + out->write( data = find6 name = `find6` ). + out->write( |\n| ). + + DATA(find7) = find( val = str sub = `es` occ = 2 ). + out->write( data = find7 name = `find7` ). + out->write( |\n| ). + + DATA(find8) = find( val = str sub = `es` occ = 3 ). + out->write( data = find8 name = `find8` ). + out->write( |\n| ). + + "pcre + DATA(find9) = find( val = str pcre = `\.` ). + out->write( data = find9 name = `find9` ). + out->write( |\n| ). + + "---------------- find_end ---------------- + "find_end returns the sum of the offset of the occurrence plus the length of the match + + DATA(find_end1) = find_end( val = str sub = `of` ). + out->write( data = find_end1 name = `find_end1` ). + out->write( |\n| ). + + DATA(find_end2) = find_end( val = str pcre = `\s` ). + out->write( data = find_end2 name = `find_end2` ). + out->write( |\n| ). + + "---------------- find_any_of ---------------- + "find_any_of returns the offset of the occurrence of any character contained + "in a substring. The search is always case-sensitive. + + DATA(find_any_of1) = find_any_of( val = str sub = `x523z4e` ). + out->write( data = find_any_of1 name = `find_any_of1` ). + out->write( |\n| ). + + DATA(find_any_of2) = find_any_of( val = str sub = `zwq85t` ). + out->write( data = find_any_of2 name = `find_any_of2` ). + out->write( |\n| ). + + "---------------- find_any_not_of ---------------- + "find_any_not_of is the negation of find_any_of + + DATA(find_any_not_of1) = find_any_not_of( val = str sub = `ieces` ). + out->write( data = find_any_not_of1 name = `find_any_not_of1` ). + out->write( |\n| ). + + DATA(find_any_not_of2) = find_any_not_of( val = str sub = `P` ). + out->write( data = find_any_not_of2 name = `find_any_not_of2` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& count, count_any_of, count_any_not_of +*&---------------------------------------------------------------------* + + DATA(st) = `Pieces of cakes.`. + + "---------------- count ---------------- + + DATA(count1) = count( val = st sub = `e` ). + out->write( data = count1 name = `count1` ). + out->write( |\n| ). + + DATA(count2) = count( val = st sub = `x` ). + out->write( data = count2 name = `count2` ). + out->write( |\n| ). + + "case (case-sensitive by default) + DATA(count3) = count( val = st sub = `p` case = abap_false ). + out->write( data = count3 name = `count3` ). + out->write( |\n| ). + + "off/len (off is 0 by default; len is the length of sting by default minus offset) + DATA(count4) = count( val = st sub = `es` off = 3 ). + out->write( data = count4 name = `count4` ). + out->write( |\n| ). + + DATA(count5) = count( val = st sub = `es` off = 9 ). + out->write( data = count5 name = `count5` ). + out->write( |\n| ). + + DATA(count6) = count( val = st sub = `es` off = 3 len = 12 ). + out->write( data = count6 name = `count6` ). + out->write( |\n| ). + + DATA(count7) = count( val = st sub = `es` len = 5 ). + out->write( data = count7 name = `count7` ). + out->write( |\n| ). + + "pcre + DATA(count8) = count( val = st pcre = `\s` ). + out->write( data = count8 name = `count8` ). + out->write( |\n| ). + + DATA(count9) = count( val = st pcre = `.` ). + out->write( data = count9 name = `count9` ). + out->write( |\n| ). + + "---------------- count_any_of ---------------- + + DATA(count_any_of1) = count_any_of( val = st sub = `x523z4e` ). + out->write( data = count_any_of1 name = `count_any_of1` ). + out->write( |\n| ). + + DATA(count_any_of2) = count_any_of( val = st sub = `eco` ). + out->write( data = count_any_of2 name = `count_any_of2` ). + out->write( |\n| ). + + "---------------- count_any_not_of ---------------- + + DATA(count_any_not_of1) = count_any_not_of( val = st sub = `fP` ). + out->write( data = count_any_not_of1 name = `count_any_not_of1` ). + out->write( |\n| ). + + DATA(count_any_not_of2) = count_any_not_of( val = st sub = `Piecs ofak.` ). + out->write( data = count_any_not_of2 name = `count_any_not_of2` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& distance +*&---------------------------------------------------------------------* + + DATA(str_to_check) = `abap`. + + DATA(dist1) = distance( val1 = str_to_check val2 = `abap` ). + out->write( data = dist1 name = `dist1` ). + out->write( |\n| ). + + DATA(dist2) = distance( val1 = str_to_check val2 = `axbap` ). + out->write( data = dist2 name = `dist2` ). + out->write( |\n| ). + + DATA(dist3) = distance( val1 = str_to_check val2 = `yabyyapy` ). + out->write( data = dist3 name = `dist3` ). + out->write( |\n| ). + + DATA(dist4) = distance( val1 = str_to_check val2 = `zabapzzzzzzzzzzzz` max = 5 ). + out->write( data = dist4 name = `dist4` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& repeat +*&---------------------------------------------------------------------* + + "abapabapabapabapabap + DATA(repeat1) = repeat( val = `abap` occ = 5 ). + out->write( data = repeat1 name = `repeat1` ). + out->write( |\n| ). + + DATA(repeat2) = |#{ repeat( val = ` ` occ = 10 ) }#|. + out->write( data = repeat2 name = `repeat2` ). + out->write( |\n| ). + + "Y (initial value returned) + DATA(repeat3) = COND #( WHEN repeat( val = `a` occ = 0 ) = `` THEN `Y` ELSE `Z` ). + out->write( data = repeat3 name = `repeat3` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& condense +*&---------------------------------------------------------------------* + + DATA(str_to_condense) = ` ab cd `. + + "No parameters specified, i. e. their default values are provided. + "Works like CONDENSE statement without the NO-GAPS addition. + DATA(condense1) = condense( str_to_condense ). + out->write( data = condense1 name = `condense1` ). + out->write( |\n| ). + + "Parameters del/to not specified. from parameter with initial string + "(could also be a text field literal: from = ' '). This way, leading and + "trailing blanks are removed. + DATA(condense2) = condense( val = str_to_condense from = `` ). + out->write( data = condense2 name = `condense2` ). + out->write( |\n| ). + + "Parameter to specified with an initial string. No other parameters. + "Works like the CONDENSE statement with the NO-GAPS addition. + DATA(condense3) = condense( val = str_to_condense to = `` ). + out->write( data = condense3 name = `condense3` ). + out->write( |\n| ). + + "Parameter del specifies the leading/trailing characters to be removed. + DATA(condense4) = condense( val = `##see###you##` del = `#` ). + out->write( data = condense4 name = `condense4` ). + out->write( |\n| ). + + "If from and to are specified along with del, leading/trailing characters + "specified in del are first removed. Then, in the remaining string, all + "substrings composed of characters specified in from are replaced with the + "first character of the string specified in the to parameter. + DATA(condense5) = condense( val = ` Rock'xxx'Roller` + del = `re ` + from = `x` + to = `n` ). + out->write( data = condense5 name = `condense5` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& concat_lines_of +*&---------------------------------------------------------------------* + + DATA(stringtable) = VALUE string_table( ( `a` ) ( `b` ) ( `c` ) ). + + DATA(con1) = concat_lines_of( table = stringtable ). + out->write( data = con1 name = `con1` ). + out->write( |\n| ). + + DATA(con2) = concat_lines_of( table = stringtable sep = ` ` ). + out->write( data = con2 name = `con2` ). + out->write( |\n| ). + + DATA(con3) = concat_lines_of( table = stringtable sep = `/` ). + out->write( data = con3 name = `con3` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& reverse +*&---------------------------------------------------------------------* + + DATA(reverse) = reverse( `paba` ). + out->write( data = reverse name = `reverse` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& escape +*&---------------------------------------------------------------------* + + "Context: URLs + DATA(esc1) = escape( val = '...test: 5@8...' + format = cl_abap_format=>e_url_full ). + out->write( data = esc1 name = `esc1` ). + out->write( |\n| ). + + "Context: JSON + DATA(esc2) = escape( val = 'some "test" json \ with backslash and double quotes' + format = cl_abap_format=>e_json_string ). + out->write( data = esc2 name = `esc2` ). + out->write( |\n| ). + + "Context: String templates + DATA(esc3) = escape( val = 'Special characters in string templates: |, \, {, }' + format = cl_abap_format=>e_string_tpl ). + out->write( data = esc3 name = `esc3` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& insert +*&---------------------------------------------------------------------* + + DATA(to_be_inserted) = `ABAP`. + + DATA(insert1) = insert( val = to_be_inserted sub = `#` ). + out->write( data = insert1 name = `insert1` ). + out->write( |\n| ). + + DATA(insert2) = insert( val = to_be_inserted sub = `#` off = 1 ). + out->write( data = insert2 name = `insert2` ). + out->write( |\n| ). + + DATA(insert3) = insert( val = to_be_inserted sub = `#` off = strlen( to_be_inserted ) ). + out->write( data = insert3 name = `insert3` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& match +*&---------------------------------------------------------------------* + + DATA(match1) = match( val = `The email address is jon.doe@email.com.` + pcre = `\w+(\.\w+)*@(\w+\.)+(\w{2,4})` ). + out->write( data = match1 name = `match1` ). + out->write( |\n| ). + + "Find blank (without inlcuding it in the result indicated by \K) and + "the following 2 characters, second occurrence + DATA(match2) = match( val = `The email address is jon.doe@email.com.` + pcre = `\s\K..` + occ = 2 ). + out->write( data = match2 name = `match2` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& replace +*&---------------------------------------------------------------------* + + DATA(to_be_replaced) = `Pieces of cakes.`. + + DATA(replace1) = replace( val = to_be_replaced sub = `es` with = `#` ). + out->write( data = replace1 name = `replace1` ). + out->write( |\n| ). + + "case + DATA(replace2) = replace( val = to_be_replaced sub = `p` case = abap_false with = `#` ). + out->write( data = replace2 name = `replace2` ). + out->write( |\n| ). + + "occ + DATA(replace3) = replace( val = to_be_replaced sub = ` ` occ = 2 with = `#` ). + out->write( data = replace3 name = `replace3` ). + out->write( |\n| ). + + "The value 0 in occ means respecting all occurrences. + DATA(replace4) = replace( val = to_be_replaced sub = `e` occ = 0 with = `#` ). + out->write( data = replace4 name = `replace4` ). + out->write( |\n| ). + + "pcre + DATA(replace5) = replace( val = to_be_replaced pcre = `\s` with = `#` ). + out->write( data = replace5 name = `replace5` ). + out->write( |\n| ). + + DATA(replace6) = replace( val = to_be_replaced pcre = `\s` occ = 2 with = `#` ). + out->write( data = replace6 name = `replace6` ). + out->write( |\n| ). + + "Replacement determined by offset/length specification only (no sub/pcre specification) + DATA(replace7) = replace( val = to_be_replaced off = 5 with = `#` ). + out->write( data = replace7 name = `replace7` ). + out->write( |\n| ). + + DATA(replace8) = replace( val = to_be_replaced len = 5 with = `#` ). + out->write( data = replace8 name = `replace8` ). + out->write( |\n| ). + + DATA(replace9) = replace( val = to_be_replaced off = 3 len = 7 with = `#` ). + out->write( data = replace9 name = `replace9` ). + out->write( |\n| ). + +*&---------------------------------------------------------------------* +*& segment +*&---------------------------------------------------------------------* + + "index: Number of segment + "sep: Substring specified is searched and used as limit + DATA(segment1) = segment( val = `Hallo,world,123` index = 1 sep = `,` ). + out->write( data = segment1 name = `segment1` ). + out->write( |\n| ). + + DATA(segment2) = segment( val = `Hallo,world,123` index = -1 sep = `,` ). + out->write( data = segment2 name = `segment2` ). + out->write( |\n| ). + + DATA(segment3) = segment( val = `HalloClass Supporting ABAP Unit Test Example
ABAP cheat sheet example class
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.
+"! Choose F9 in ADT to run the class.
Find information on getting started with the example class and the disclaimer in +"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.
+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. diff --git a/src/zcl_demo_abap_unit_dataprov.clas.xml b/src/zcl_demo_abap_unit_dataprov.clas.xml new file mode 100644 index 0000000..4fc439b --- /dev/null +++ b/src/zcl_demo_abap_unit_dataprov.clas.xml @@ -0,0 +1,16 @@ + +Creating Test Doubles Using ABAP Frameworks
ABAP cheat sheet example class
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.
+"! Choose F9 in ADT to run the class. To run all unit tests of the class, choose Ctrl/Cmd + Shift + F10.
+"! If the unit tests have not yet run, right-click the Foreign Tests entry in the ABAP Unit tab of ADT,
+"! and choose Run.
Find information on getting started with the example class and the disclaimer in +"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.
+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. diff --git a/src/zcl_demo_abap_unit_tdf.clas.xml b/src/zcl_demo_abap_unit_tdf.clas.xml new file mode 100644 index 0000000..912356b --- /dev/null +++ b/src/zcl_demo_abap_unit_tdf.clas.xml @@ -0,0 +1,16 @@ + +Test Class Supporting an ABAP Unit Test Example
ABAP cheat sheet example class
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.
+"! This global class is empty, but the the Test Classes tab includes a test class. Using the syntax
+"! "! @testing ..., 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.
Find information on getting started with the example class and the disclaimer in +"! the ABAP Doc comment of class {@link zcl_demo_abap_aux}.
+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. diff --git a/src/ztcl_demo_abap_unit_tdf_testcl.clas.testclasses.abap b/src/ztcl_demo_abap_unit_tdf_testcl.clas.testclasses.abap new file mode 100644 index 0000000..d0ff5af --- /dev/null +++ b/src/ztcl_demo_abap_unit_tdf_testcl.clas.testclasses.abap @@ -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. diff --git a/src/ztcl_demo_abap_unit_tdf_testcl.clas.xml b/src/ztcl_demo_abap_unit_tdf_testcl.clas.xml new file mode 100644 index 0000000..5b5f7eb --- /dev/null +++ b/src/ztcl_demo_abap_unit_tdf_testcl.clas.xml @@ -0,0 +1,17 @@ + +