From e0d61e3d12d01d079a33d9223b61614f5c674d9d Mon Sep 17 00:00:00 2001 From: danrega <16720986+danrega@users.noreply.github.com> Date: Fri, 24 Feb 2023 15:23:59 +0100 Subject: [PATCH] Update content --- 01_Internal_Tables.md | 4 +- 14_ABAP_Unit_Tests.md | 91 +- src/zcl_demo_abap_unit_test.clas.abap | 862 +++++----- ..._demo_abap_unit_test.clas.testclasses.abap | 1460 ++++++++++------- 4 files changed, 1405 insertions(+), 1012 deletions(-) diff --git a/01_Internal_Tables.md b/01_Internal_Tables.md index 80f6ba9..11d3c6c 100644 --- a/01_Internal_Tables.md +++ b/01_Internal_Tables.md @@ -136,7 +136,7 @@ - are optional for all table categories. - can be unique/non-unique sorted keys or unique hash keys. - have a self-defined name. An alias name can also be specified. -- A secondary table index is created internally for each sorted secondary key. This allows index access to hashed tables via the secondary table key is possible. In this case, `sy-tabix` is set. +- A secondary table index is created internally for each sorted secondary key. This allows index access to hashed tables via the secondary table key. In this case, `sy-tabix` is set. - When accessing internal tables using the secondary table key, the key name (or the alias if specified) must be specified. They are not selected automatically. If no secondary key is specified in a processing statement, the primary key or primary table index is always used. If you want to make use of this key in ABAP statements, for example, `READ`, `LOOP AT` or `MODIFY` statements, you must specify the key explicitly using the appropriate additions, for example, `WITH ... KEY ... COMPONENTS` or `USING KEY`. - Use cases: - To improve read performance. @@ -1416,7 +1416,7 @@ the content are removed, but the memory space initially requested remains allocated. If the table is filled again later, the memory space is still available, which is a performance advantag over clearing an internal table with `FREE`. Such a statement also -deleted the table content, but it also releases the memory +deletes the table content, but it also releases the memory space. ``` abap diff --git a/14_ABAP_Unit_Tests.md b/14_ABAP_Unit_Tests.md index 312802a..cdeac25 100644 --- a/14_ABAP_Unit_Tests.md +++ b/14_ABAP_Unit_Tests.md @@ -422,94 +422,13 @@ There are multiple ways to implement test doubles manually: **Injecting the test doubles** As described [here](https://help.sap.com/docs/ABAP_PLATFORM_NEW/c238d694b825421f940829321ffa326a/04a2d0fc9cd940db8aedf3fa29e5f07e.html?locale=en-US), there are multiple techniques for injecting test doubles to ensure that the test doubles are used during the test run. -To name two of them: -- Constructor injection -- Back door injection - -*Constructor injection* -- This injection mechanism means that the test double is passed as a parameter to the instance constructor `constructor` of the class under test. -- `constructor` declaration: - - Has an optional importing parameter for the DOC. - - The parameter is typed with reference to the test double, i.e. an object of the test double is passed. -- `constructor` implementation: - - In the example beloew, it is assumed that an interface exists for the DOC, and a test double has been implemented that implements that interface. - - A reference variable with a type reference to the interface is declared in the declaration part of the class under test. - - When the unit test is executed, an object of the test double is created in the test method. This object is then passed to the `constructor`. A check is implemented to determine if the reference variable is bound. During the unit test execution, it is indeed bound, and the test double is injected. If the unit test is not run and the class is executed, an object of the *regular data provider* (e.g. a class that implements the interface for production use) is created. Therefore, it is ensured that the test double is only used in the context of a unit test run. - -Such a constructor injection might look like this. - -``` abap -"See the executable example for the complete picture. -"The code in this snippet refers to the production code. -... -"Class declaration part - DATA ref_data_provider TYPE REF TO if_data, - - "Optional parameter for passing an object of the test touble - METHODS constructor - IMPORTING iref_data_prov TYPE REF TO tld_test_double OPTIONAL. - -.... - -"Class implementation part -METHOD constructor. - "If the object of the test touble is not passed, an object is created for the actual data provider - - IF iref_data_prov IS BOUND. - - "Note: The parameter is only bound when running in ABAP unit test - ref_data_provider = iref_data_prov. +Among them, there are the following. They are demonstrated in the executable example. Check the code and comments in the [global class](./src/zcl_demo_abap_unit_test.clas.abap) and [test include](./src/zcl_demo_abap_unit_test.clas.testclasses.abap) of the example. +- Constructor injection: The test double is passed as a parameter to the instance constructor `constructor` of the class under test. +- Setter injection: The test double is passed as a parameter to a setter method. +- Parameter injection: The test double is passed as a parameter to the tested method (i.e. an optional importing parameter) in the class under test. +- Back door injection: A *back door* is created to inject a test double into the class under test. This *back door* is implemented by granting [friendship](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfriend_glosry.htm) to the test class. This makes internal attributes of the class under test accessible from the test class. - ELSE. - - ref_data_provider = NEW cl_data_provider( ). - - ENDIF. -ENDMETHOD. -``` - - -*Back door injection* -- This injection mechanism means that a *back door* is created to inject a test double into the class under test. -- This *back door* is implemented by granting [friendship](https://help.sap.com/doc/abapdocu_cp_index_htm/CLOUD/en-US/index.htm?file=abenfriend_glosry.htm) to the test class. This makes internal attributes of the class under test accessible from the test class. -- The back door injection enters the picture when internal attributes of the class under test are changed during the test run. That is, in the production code, you have declared a reference variable with a type reference to the data provider in the private section, for example. - - Note: Similar to above, in the example below, it is assumed that there is an interface for the DOC and that a test double has been implemented that implements that interface. -- When the unit test is executed, the private attribute of the class under test is changed, and an object of the test double is injected. - - -``` abap -"See the executable example for the complete picture. -"The code in this snippet refers to the test class. - -"Test class declaration part -... - -"class under test -DATA ref_cut TYPE REF TO cl_class_under_test. - -"if_data_provider: interface with which local test data are created -"by implementing an interface method -DATA ref_data_prov TYPE REF TO if_data_provider. - -... - - "Implementation of the setup method in the test class - METHOD setup. - - ref_cut = NEW #( ). - - "Assumption: The local test double ltd_test_double implements the interface if_data_provider - ref_data_prov = new ltd_test_double( ). - - "Back door injection - "The reference variable declared in the class under test is assigned an object - "of the test double. In the class under test, the reference variable is defined - "with type TYPE REF TO if_data_provider. - ref_cut->ref_var_defined_in_cut = ref_data_prov. - ENDMETHOD. -``` -

(back to top)

### Test Seams diff --git a/src/zcl_demo_abap_unit_test.clas.abap b/src/zcl_demo_abap_unit_test.clas.abap index e1d1fcd..bc4fe7f 100644 --- a/src/zcl_demo_abap_unit_test.clas.abap +++ b/src/zcl_demo_abap_unit_test.clas.abap @@ -54,483 +54,613 @@ "! Example to demonstrate ABAP unit tests.
Choose F9 in ADT to run the class. "! To run all unit tests of the class, choose Ctrl/Cmd + Shift + F10. CLASS zcl_demo_abap_unit_test DEFINITION - PUBLIC - CREATE PUBLIC. +PUBLIC +CREATE PUBLIC. - PUBLIC SECTION. +PUBLIC SECTION. - INTERFACES: if_oo_adt_classrun. + INTERFACES: if_oo_adt_classrun. - CLASS-METHODS: class_constructor. + CLASS-METHODS: class_constructor. - "Optional parameter for the instance constructor for the purpose of - "constructor injection - METHODS constructor - IMPORTING iref_data_prov TYPE REF TO zdemo_abap_get_data_itf OPTIONAL. + "Optional parameter for the instance constructor for the purpose of + "constructor injection + METHODS constructor + IMPORTING iref_data_prov TYPE REF TO zdemo_abap_get_data_itf OPTIONAL. - PROTECTED SECTION. +PROTECTED SECTION. - TYPES carr_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY. + TYPES carr_tab TYPE TABLE OF zdemo_abap_fli WITH EMPTY KEY. - METHODS: select_flight_data IMPORTING carrier TYPE zdemo_abap_fli-carrid - RETURNING VALUE(flight_data) TYPE carr_tab. + METHODS: select_flight_data IMPORTING carrier TYPE zdemo_abap_fli-carrid + RETURNING VALUE(flight_data) TYPE carr_tab. - PRIVATE SECTION. +PRIVATE SECTION. - TYPES: int_tab_so TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line, - int_tab_st TYPE STANDARD TABLE OF i WITH EMPTY KEY, - occ_rate TYPE p LENGTH 4 DECIMALS 2, + TYPES: int_tab_so TYPE SORTED TABLE OF i WITH UNIQUE KEY table_line, + int_tab_st TYPE STANDARD TABLE OF i WITH EMPTY KEY, + occ_rate TYPE p LENGTH 4 DECIMALS 2, - BEGIN OF nums_struc, - num1 TYPE i, - num2 TYPE i, - END OF nums_struc, + BEGIN OF nums_struc, + num1 TYPE i, + num2 TYPE i, + END OF nums_struc, - nums_tab TYPE TABLE OF nums_struc WITH EMPTY KEY. + nums_tab TYPE TABLE OF nums_struc WITH EMPTY KEY. - DATA: seats_table TYPE zdemo_abap_get_data_itf=>carr_tab, - flight_tab TYPE TABLE OF zdemo_abap_fli, + DATA: seats_table TYPE zdemo_abap_get_data_itf=>carr_tab, + flight_tab TYPE TABLE OF zdemo_abap_fli, - "Reference variable for back door injection - "Note: In the example, it is a local interface declared in the - "Local Types tab (the CCIMP include). To make the type known to - "the global class, see the Class-relevant Local Types tab (the CCDEF include). - data_provider_local_itf TYPE REF TO lif_get_data, + "Reference variable for back door injection + "Note: In the example, it is a local interface declared in the + "Local Types tab (the CCIMP include). To make the type known to + "the global class, see the Class-relevant Local Types tab (the CCDEF include). + data_provider_local_itf TYPE REF TO lif_get_data, - "Reference variable for constructor injection - "In the example, the type refers to a global interface. - data_provider_global_itf TYPE REF TO zdemo_abap_get_data_itf. + "Reference variable for constructor injection + "In the example, the type refers to a global interface. + data_provider_global_itf TYPE REF TO zdemo_abap_get_data_itf, - METHODS: - "Calculates the sum of two numbers - "This method demonstrates the use of the setup and teardown methods in the test class. - get_sum IMPORTING key TYPE zdemo_abap_tab1-key_field - char TYPE zdemo_abap_tab1-char1 - RETURNING VALUE(sum) TYPE i, + "For demonstrating setter injection + data_provider_setter_inj TYPE REF TO zdemo_abap_get_data_itf, - "Calculates the common divisors and the greatest common divisor of two numbers - get_common_div_and_gcd IMPORTING a TYPE i - b TYPE i - EXPORTING common_divisors TYPE int_tab_so - gcd TYPE i, + "For demonstrating parameter injection + data_provider_param_inj TYPE REF TO zdemo_abap_get_data_itf.. - "Calculates the digit sum of a number - get_digit_sum IMPORTING num TYPE i - RETURNING VALUE(digit_sum) TYPE i, + METHODS: + "Calculates the sum of two numbers + "This method demonstrates the use of the setup and teardown methods in the test class. + get_sum IMPORTING key TYPE zdemo_abap_tab1-key_field + char TYPE zdemo_abap_tab1-char1 + RETURNING VALUE(sum) TYPE i, - "Multiple methods that all do the same (they calculate the occupancy rate of flights) - "but serve different demonstration purposes for the ABAP unit tests in the example. - "The method implementations involve a depended-on component (DOC). In this case, - "it is a database access. + "Calculates the common divisors and the greatest common divisor of two numbers + get_common_div_and_gcd IMPORTING a TYPE i + b TYPE i + EXPORTING common_divisors TYPE int_tab_so + gcd TYPE i, - "Method to demonstrate test double injection using inheritance and method redefinition - get_occ_rate_using_meth IMPORTING carrier_id TYPE zdemo_abap_fli-carrid - RETURNING VALUE(occupancy_rate) TYPE occ_rate, + "Calculates the digit sum of a number + get_digit_sum IMPORTING num TYPE i + RETURNING VALUE(digit_sum) TYPE i, - "Method to demonstrate test double injection using test seams - get_occ_rate_test_seam IMPORTING carrier_id TYPE zdemo_abap_fli-carrid - EXPORTING occupancy_rate TYPE occ_rate - num1 TYPE i - num2 TYPE i, + "Multiple methods that all do the same (they calculate the occupancy rate of flights) + "but serve different demonstration purposes for the ABAP unit tests in the example. + "The method implementations involve a depended-on component (DOC). In this case, + "it is a database access. + "The methods are intentionally implemented in a similar way. Therefore, almost + "all of the following methods will display the same output in the console when the + "class is executed using F9. - "Method to demonstrate test double injection using back door injection and a local interface - get_occ_rate_local_itf IMPORTING carrier_id TYPE zdemo_abap_fli-carrid - RETURNING VALUE(occupancy_rate) TYPE occ_rate, + "Method to demonstrate test double injection using inheritance and method redefinition + get_occ_rate_using_meth IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, - "Method to demonstrate test double injection using constructor injection and a global interface - get_occ_rate_global_itf IMPORTING carrier_id TYPE zdemo_abap_fli-carrid - RETURNING VALUE(occupancy_rate) TYPE occ_rate. + "Method to demonstrate test double injection using test seams + get_occ_rate_test_seam IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + EXPORTING occupancy_rate TYPE occ_rate + num1 TYPE i + num2 TYPE i, + + "Method to demonstrate test double injection using back door injection and a local interface + get_occ_rate_local_itf IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method to demonstrate test double injection using constructor injection and a global interface + get_occ_rate_global_itf IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method to demonstrate test double injection using setter injection and a global interface + get_occ_rate_setter_inj IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + RETURNING VALUE(occupancy_rate) TYPE occ_rate, + + "Method for setter injection + setter_meth IMPORTING data_prov TYPE REF TO zdemo_abap_get_data_itf, + + "Method to demonstrate test double injection using parameter injection and a global interface + "An optional parameter is specified for passing the test double if the method is tested. + get_occ_rate_param_inj IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + data_prov TYPE REF TO zdemo_abap_get_data_itf OPTIONAL + RETURNING VALUE(occupancy_rate) TYPE occ_rate. ENDCLASS. CLASS zcl_demo_abap_unit_test IMPLEMENTATION. - METHOD if_oo_adt_classrun~main. - "Note: The example includes a couple of implementations for the methods - "declared above. And by choosing F9 in ADT, you can run the class and check the - "output in the console. - "However, the focus of the example is unit tests. Therefore, check the - "test classes and methods in the test include (Test Classes tab in ADT). - "Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class and check the - "test results in the ABAP Unit tab in ADT. +METHOD if_oo_adt_classrun~main. + "Note: The example includes a couple of implementations for the methods + "declared above. And by choosing F9 in ADT, you can run the class and check the + "output in the console. + "However, the focus of the example is unit tests. Therefore, check the + "test classes and methods in the test include (Test Classes tab in ADT). + "Choose Ctrl/Cmd + Shift + F10 to launch all tests in the class and check the + "test results in the ABAP Unit tab in ADT. - DATA(output) = NEW zcl_demo_abap_display( out ). + DATA(output) = NEW zcl_demo_abap_display( out ). - output->display( `ABAP Cheat Sheet Demonstration Example: A Glimpse on ABAP Unit Tests` ). - output->display( `1) get_sum Method` ). - "This method demonstrates the use of the setup and teardown methods in the test class. + output->display( `ABAP Cheat Sheet Demonstration Example: A Glimpse on ABAP Unit Tests` ). + output->display( `1) get_sum Method` ). + "This method demonstrates the use of the setup and teardown methods in the test class. - DATA(sum) = get_sum( key = 1 char = 'aaa' ). + DATA(sum) = get_sum( key = 1 char = 'aaa' ). - output->display( input = sum name = `sum` ). + output->display( input = sum name = `sum` ). ********************************************************************** - output->next_section( `2) get_common_div_and_gcd Method` ). + output->next_section( `2) get_common_div_and_gcd Method` ). - "Filling an internal table with numbers on whose bases the common divisors and the - "greatest common divisor are to be calculated - DATA(tab) = VALUE nums_tab( ( num1 = 10 num2 = 20 ) - ( num1 = 100 num2 = 200 ) - ( num1 = 12 num2 = 6 ) - ( num1 = 5 num2 = 1 ) - ( num1 = 50 num2 = 50 ) - ( num1 = 4 num2 = 8 ) ). + "Filling an internal table with numbers on whose bases the common divisors and the + "greatest common divisor are to be calculated + DATA(tab) = VALUE nums_tab( ( num1 = 10 num2 = 20 ) + ( num1 = 100 num2 = 200 ) + ( num1 = 12 num2 = 6 ) + ( num1 = 5 num2 = 1 ) + ( num1 = 50 num2 = 50 ) + ( num1 = 4 num2 = 8 ) ). - LOOP AT tab ASSIGNING FIELD-SYMBOL(). + LOOP AT tab ASSIGNING FIELD-SYMBOL(). - get_common_div_and_gcd( EXPORTING a = -num1 - b = -num2 - IMPORTING common_divisors = DATA(common_divs) gcd = DATA(gcd) ). + get_common_div_and_gcd( EXPORTING a = -num1 + b = -num2 + IMPORTING common_divisors = DATA(common_divs) gcd = DATA(gcd) ). - output->display( |Common divisors of { -num1 } and { -num2 }| ). + output->display( |Common divisors of { -num1 } and { -num2 }| ). - output->display( input = common_divs name = `common_divs` ). + output->display( input = common_divs name = `common_divs` ). - output->display( |Greatest common divisor of { -num1 } and { -num2 }: { gcd } | ). + output->display( |Greatest common divisor of { -num1 } and { -num2 }: { gcd } | ). - ENDLOOP. + ENDLOOP. ********************************************************************** - output->next_section( `3) get_digit_sum Method` ). + output->next_section( `3) get_digit_sum Method` ). - "Filling an internal table with numbers on whose bases the digit sum is to be calculated - DATA(tab_i) = VALUE int_tab_so( ( 12 ) - ( 123 ) - ( 3 ) - ( 8246 ) - ( 1001001 ) - ( 0 ) ). + "Filling an internal table with numbers on whose bases the digit sum is to be calculated + DATA(tab_i) = VALUE int_tab_so( ( 12 ) + ( 123 ) + ( 3 ) + ( 8246 ) + ( 1001001 ) + ( 0 ) ). - LOOP AT tab_i ASSIGNING FIELD-SYMBOL(). + LOOP AT tab_i ASSIGNING FIELD-SYMBOL(). - DATA(digit_sum) = get_digit_sum( ). + DATA(digit_sum) = get_digit_sum( ). - output->display( |The digit sum of { } is { digit_sum }.| ). + output->display( |The digit sum of { } is { digit_sum }.| ). - ENDLOOP. + ENDLOOP. ********************************************************************** - output->next_section( `4) get_occ_rate_using_meth Method` ). - "In the test class, this method demonstrates test double injection - "using inheritance and method redefinition. + output->next_section( `4) get_occ_rate_using_meth Method` ). + "In the test class, this method demonstrates test double injection + "using inheritance and method redefinition. - "Filling an internal table with carrier ids on whose bases the occupancy - "rate is to be calculated. - DATA(tab_str) = VALUE zdemo_abap_get_data_itf=>carr_tab( ( carrid = 'LH' ) - ( carrid = 'AA' ) - ( carrid = 'DL' ) ). + "Filling an internal table with carrier ids on whose bases the occupancy + "rate is to be calculated. + DATA(tab_str) = VALUE zdemo_abap_get_data_itf=>carr_tab( ( carrid = 'LH' ) + ( carrid = 'AA' ) + ( carrid = 'DL' ) ). - LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). - DATA(occupancy_rate) = get_occ_rate_using_meth( -carrid ). + DATA(occupancy_rate) = get_occ_rate_using_meth( -carrid ). - output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate }%.| ). + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate }%.| ). - ENDLOOP. - output->next_section( `5) get_occ_rate_test_seam Method` ). - "This method demonstrates test double injection using test seams. + ENDLOOP. + output->next_section( `5) get_occ_rate_test_seam Method` ). + "This method demonstrates test double injection using test seams. - LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). - get_occ_rate_test_seam( EXPORTING carrier_id = -carrid - IMPORTING occupancy_rate = DATA(occupancy_rate_test_seam) - num1 = DATA(num1) - num2 = DATA(num2) ). + get_occ_rate_test_seam( EXPORTING carrier_id = -carrid + IMPORTING occupancy_rate = DATA(occupancy_rate_test_seam) + num1 = DATA(num1) + num2 = DATA(num2) ). - output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_test_seam }%.| ). + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_test_seam }%.| ). - output->display( |num1: { num1 }| ). - output->display( |num2: { num2 }| ). + output->display( |num1: { num1 }| ). + output->display( |num2: { num2 }| ). - ENDLOOP. + ENDLOOP. ********************************************************************** - output->next_section( `6) get_occ_rate_local_itf Method` ). - "This method demonstrates test double injection using back door - "injection and a local interface. + output->next_section( `6) get_occ_rate_local_itf Method` ). + "This method demonstrates test double injection using back door + "injection and a local interface. - LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). - DATA(occupancy_rate_local_itf) = get_occ_rate_local_itf( -carrid ). + DATA(occupancy_rate_local_itf) = get_occ_rate_local_itf( -carrid ). - output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_local_itf }%.| ). + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_local_itf }%.| ). - ENDLOOP. + ENDLOOP. ********************************************************************** - output->next_section( `7) get_occ_rate_global_itf Method` ). - "This method demonstrates test double injection using constructor - "injection and a global interface. + output->next_section( `7) get_occ_rate_global_itf Method` ). + "This method demonstrates test double injection using constructor + "injection and a global interface. - LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). - DATA(occupancy_rate_global_itf) = get_occ_rate_global_itf( -carrid ). + DATA(occupancy_rate_global_itf) = get_occ_rate_global_itf( -carrid ). - output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_global_itf }%.| ). + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_global_itf }%.| ). - ENDLOOP. + ENDLOOP. - ENDMETHOD. +********************************************************************** - METHOD class_constructor. - "Filling demo database tables. - zcl_demo_abap_flight_tables=>fill_dbtabs( ). + output->next_section( `8) get_occ_rate_setter_inj Method` ). + "This method demonstrates test double injection using setter injection and a global interface. - "Preparing a demo database table for this example (get_sum method) - DELETE FROM zdemo_abap_tab1. - INSERT zdemo_abap_tab1 FROM @( - VALUE #( key_field = 1 char1 = 'aaa' char2 = 'bbb' num1 = 25 num2 = 75 ) ). - ENDMETHOD. + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). - METHOD constructor. + DATA(occupancy_rate_setter_inj) = get_occ_rate_setter_inj( -carrid ). - "For demonstrating the back door injection - data_provider_local_itf = NEW lcl_data_prov_local_itf( ). + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_setter_inj }%.| ). - "For demonstrating the constructor injection - IF iref_data_prov IS BOUND. - "Note: The parameter is only bound when you run the unit test. - "When you run the unit test and you debug, you will see that iref_data_prov - "has a type reference to LTD_TEST_DATA_GLOBAL_INTF. + ENDLOOP. - data_provider_global_itf = iref_data_prov. +********************************************************************** - ELSE. + output->next_section( `9) get_occ_rate_param_inj Method` ). + "This method demonstrates test double injection using parameter injection and a global interface. - data_provider_global_itf = NEW lcl_data_prov_glo_itf( ). + LOOP AT tab_str ASSIGNING FIELD-SYMBOL(). + DATA(occupancy_rate_param_inj) = get_occ_rate_param_inj( carrier_id = -carrid ). + + output->display( |The occupancy rate for airline { -carrid } is { occupancy_rate_param_inj }%.| ). + + ENDLOOP. + + +ENDMETHOD. + +METHOD class_constructor. + "Filling demo database tables. + zcl_demo_abap_flight_tables=>fill_dbtabs( ). + + "Preparing a demo database table for this example (get_sum method) + DELETE FROM zdemo_abap_tab1. + INSERT zdemo_abap_tab1 FROM @( + VALUE #( key_field = 1 char1 = 'aaa' char2 = 'bbb' num1 = 25 num2 = 75 ) ). +ENDMETHOD. + +METHOD constructor. + + "For demonstrating the back door injection + data_provider_local_itf = NEW lcl_data_prov_local_itf( ). + + "For demonstrating the constructor injection + IF iref_data_prov IS BOUND. + "Note: The parameter is only bound when you run the unit test. + "When you run the unit test and you debug, you will see that iref_data_prov + "has a type reference to LTD_TEST_DATA_GLOBAL_INTF. + + data_provider_global_itf = iref_data_prov. + + ELSE. + + data_provider_global_itf = NEW lcl_data_prov_glo_itf( ). + + ENDIF. + + "Object creation for the method call in the get_occ_rate_setter_inj method + data_provider_setter_inj = NEW lcl_data_prov_glo_itf( ). + +ENDMETHOD. + +METHOD get_sum. + "The method selects a record from a database table and sums the values + "of two fields, both are of type i. + + SELECT SINGLE + FROM zdemo_abap_tab1 + FIELDS num1 + num2 AS sum + WHERE key_field = @key + AND char1 = @char + INTO @sum. + +ENDMETHOD. + +METHOD get_common_div_and_gcd. + "Calculates the common divisors and the greatest common divisor of two numbers + + CLEAR: common_divisors, gcd. + + CHECK a >= 1. + CHECK b >= 1. + + IF a >= b. + DATA(greater_num) = a. + DATA(lower_num) = b. + ELSE. + greater_num = b. + lower_num = a. + ENDIF. + + "Getting common divisors + DATA(div) = 1. + + WHILE div <= lower_num. + IF lower_num MOD div = 0. + DATA(divisor) = lower_num / div. + INSERT divisor INTO TABLE common_divisors. ENDIF. - ENDMETHOD. + div += 1. + ENDWHILE. - METHOD get_sum. - "The method selects a record from a database table and sums the values - "of two fields, both are of type i. + LOOP AT common_divisors ASSIGNING FIELD-SYMBOL(). - SELECT SINGLE - FROM zdemo_abap_tab1 - FIELDS num1 + num2 AS sum - WHERE key_field = @key - AND char1 = @char - INTO @sum. - - ENDMETHOD. - - METHOD get_common_div_and_gcd. - "Calculates the common divisors and the greatest common divisor of two numbers - - CLEAR: common_divisors, gcd. - - CHECK a >= 1. - CHECK b >= 1. - - IF a >= b. - DATA(greater_num) = a. - DATA(lower_num) = b. - ELSE. - greater_num = b. - lower_num = a. + IF greater_num MOD <> 0. + DELETE common_divisors WHERE table_line = . ENDIF. - "Getting common divisors - DATA(div) = 1. + ENDLOOP. - WHILE div <= lower_num. - IF lower_num MOD div = 0. - DATA(divisor) = lower_num / div. - INSERT divisor INTO TABLE common_divisors. - ENDIF. + "Extracting the greatest common divisor from the list of common divisors + gcd = common_divisors[ lines( common_divisors ) ]. - div += 1. - ENDWHILE. +ENDMETHOD. - LOOP AT common_divisors ASSIGNING FIELD-SYMBOL(). +METHOD get_digit_sum. + "Calculates the digit sum of a number - IF greater_num MOD <> 0. - DELETE common_divisors WHERE table_line = . - ENDIF. + CLEAR digit_sum. - ENDLOOP. + CHECK num >= 0. - "Extracting the greatest common divisor from the list of common divisors - gcd = common_divisors[ lines( common_divisors ) ]. + DATA(converted_int) = CONV string( num ). + DATA(len) = strlen( converted_int ). - ENDMETHOD. + DO len TIMES. + DATA(idx) = sy-index - 1. + digit_sum = digit_sum + converted_int+idx(1). + ENDDO. - METHOD get_digit_sum. - "Calculates the digit sum of a number +ENDMETHOD. - CLEAR digit_sum. - - CHECK num >= 0. - - DATA(converted_int) = CONV string( num ). - DATA(len) = strlen( converted_int ). - - DO len TIMES. - DATA(idx) = sy-index - 1. - digit_sum = digit_sum + converted_int+idx(1). - ENDDO. - - ENDMETHOD. - - METHOD get_occ_rate_test_seam. - "Method to demonstrate test double injection using test seams - "Note: The code is just for demonstration purposes. Of course, the result can be - "achieved more elegantly using SQL expressions, for example. - - TEST-SEAM select_flights. - "DOC - SELECT seatsmax, seatsocc - FROM zdemo_abap_fli - WHERE carrid = @carrier_id - INTO CORRESPONDING FIELDS OF TABLE @seats_table. - END-TEST-SEAM. - - DATA total_seatsmax_tm TYPE i. - DATA total_seatsocc_tm TYPE i. - - LOOP AT seats_table ASSIGNING FIELD-SYMBOL(). - - total_seatsmax_tm = total_seatsmax_tm + -seatsmax. - total_seatsocc_tm = total_seatsocc_tm + -seatsocc. - - ENDLOOP. - - occupancy_rate = total_seatsocc_tm / total_seatsmax_tm * 100. - - "Further examples for test seams - DATA(var) = 0. - - "Empty test seam; code is injected during unit test - "Check the output when running the class using F9 and - "the test results when running the unit test. - TEST-SEAM num1. - END-TEST-SEAM. - - IF var = 0. - num1 = 1. - ELSE. - num1 = 999. - ENDIF. - - num2 = 0. - - "Empty injection - "See the test class: The code that is included in the test - "seam should be excluded from the test. Therefore, the - "test injection block in the test class is empty. - "Check the output when running the class using F9 and - "the test results when running the unit test. - TEST-SEAM num2. - num2 = 123. - END-TEST-SEAM. - - ENDMETHOD. - - METHOD get_occ_rate_local_itf. - "Method to demonstrate test double injection using back door - "injection and a local interface - - DATA total_seatsmax_local_itf TYPE i. - DATA total_seatsocc_local_itf TYPE i. - - "Assumption: The original code in this method was as follows (the line commented out). - "It was identified as DOC (reading data from a database table). - - "DATA(flight_data) = select_flight_data( carrier = carrier_id ). - - "Instead of a method call like above and for a proper unit testing - a global interface - "is not available - a local interface is created, and - "an interface method is implemented. In this example, the local interface is - "created in the local types tab (CCIMP include): lif_get_data - "A local class (lcl_data_prov_local_itf) is also created in the CCIMP include. It - "implements the local interface. - - "When running the class with F9, the object used here refers to type lcl_data_prov_local_itf. - "When running the unit test, the object used here refers to type ltd_test_data_local_itf, - "i.e. the local test double is injected. - DATA(flight_data) = data_provider_local_itf->select_flight_data( carrier = carrier_id ). - - LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). - - total_seatsmax_local_itf = total_seatsmax_local_itf + -seatsmax. - total_seatsocc_local_itf = total_seatsocc_local_itf + -seatsocc. - - ENDLOOP. - - occupancy_rate = total_seatsocc_local_itf / total_seatsmax_local_itf * 100. - - ENDMETHOD. - - METHOD get_occ_rate_global_itf. - "Method to demonstrate test double injection using constructor injection - "and a global interface - - DATA total_seatsmax_global_itf TYPE i. - DATA total_seatsocc_global_itf TYPE i. - - "Assumption: The original code in this method was as follows (the line commented out). - "It was identified as DOC (reading data from a database table). - - "DATA(flight_data) = select_flight_data( carrier = carrier_id ). - - "Instead of a method call like above and for a proper unit testing, a global interface - "is provided. - "In the example, an interface method is implemented in a local class in the local types - "tab (CCIMP include): lcl_data_prov_glo_itf - - "When running the class with F9, the object used here refers to type lcl_data_prov_glo_itf. - "When running the unit test, the object used here refers to type ltd_test_data_global_intf, - "i.e. the local test double is injected. - - DATA(flight_data) = data_provider_global_itf->select_flight_data( carrier = carrier_id ). - - LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). - - total_seatsmax_global_itf = total_seatsmax_global_itf + -seatsmax. - total_seatsocc_global_itf = total_seatsocc_global_itf + -seatsocc. - - ENDLOOP. - - occupancy_rate = total_seatsocc_global_itf / total_seatsmax_global_itf * 100. - - ENDMETHOD. - - METHOD select_flight_data. - "Method that is identified as DOC in the method implementations above. - "This method is also used for demonstrating test double injection and method redefinition. +METHOD get_occ_rate_test_seam. +"Method to demonstrate test double injection using test seams +"Note: The code is just for demonstration purposes. Of course, the result can be +"achieved more elegantly using SQL expressions, for example. + TEST-SEAM select_flights. + "DOC SELECT seatsmax, seatsocc FROM zdemo_abap_fli - WHERE carrid = @carrier - INTO CORRESPONDING FIELDS OF TABLE @flight_data. - ENDMETHOD. + WHERE carrid = @carrier_id + INTO CORRESPONDING FIELDS OF TABLE @seats_table. + END-TEST-SEAM. - METHOD get_occ_rate_using_meth. - "This method demonstrates test double injection using inheritance and method redefinition. + DATA total_seatsmax_tm TYPE i. + DATA total_seatsocc_tm TYPE i. - DATA total_seatsmax_no TYPE i. - DATA total_seatsocc_no TYPE i. + LOOP AT seats_table ASSIGNING FIELD-SYMBOL(). - "During the unit test, the redefined method in the test class is called. - DATA(flight_data) = select_flight_data( carrier = carrier_id ). + total_seatsmax_tm = total_seatsmax_tm + -seatsmax. + total_seatsocc_tm = total_seatsocc_tm + -seatsocc. - LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + ENDLOOP. - total_seatsmax_no = total_seatsmax_no + -seatsmax. - total_seatsocc_no = total_seatsocc_no + -seatsocc. + occupancy_rate = total_seatsocc_tm / total_seatsmax_tm * 100. - ENDLOOP. + "Further examples for test seams + DATA(var) = 0. - occupancy_rate = total_seatsocc_no / total_seatsmax_no * 100. + "Empty test seam; code is injected during unit test + "Check the output when running the class using F9 and + "the test results when running the unit test. + TEST-SEAM num1. + END-TEST-SEAM. - ENDMETHOD. + IF var = 0. + num1 = 1. + ELSE. + num1 = 999. + ENDIF. -ENDCLASS. + num2 = 0. + + "Empty injection + "See the test class: The code that is included in the test + "seam should be excluded from the test. Therefore, the + "test injection block in the test class is empty. + "Check the output when running the class using F9 and + "the test results when running the unit test. + TEST-SEAM num2. + num2 = 123. + END-TEST-SEAM. + +ENDMETHOD. + +METHOD get_occ_rate_local_itf. + "Method to demonstrate test double injection using back door + "injection and a local interface + + DATA total_seatsmax_local_itf TYPE i. + DATA total_seatsocc_local_itf TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing - a global interface + "is not available - a local interface is created, and + "an interface method is implemented. In this example, the local interface is + "created in the local types tab (CCIMP include): lif_get_data + "A local class (lcl_data_prov_local_itf) is also created in the CCIMP include. It + "implements the local interface. + + "When the class is executed using F9, the object used here refers to type lcl_data_prov_local_itf. + "When the unit test is executed, the object used here refers to type ltd_test_data_local_itf, + "i.e. the local test double is injected. + DATA(flight_data) = data_provider_local_itf->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_local_itf = total_seatsmax_local_itf + -seatsmax. + total_seatsocc_local_itf = total_seatsocc_local_itf + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_local_itf / total_seatsmax_local_itf * 100. + +ENDMETHOD. + +METHOD get_occ_rate_global_itf. + "Method to demonstrate test double injection using constructor injection + "and a global interface + + DATA total_seatsmax_global_itf TYPE i. + DATA total_seatsocc_global_itf TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing, a global interface + "is provided. + "In the example, an interface method is implemented in a local class in the local types + "tab (CCIMP include): lcl_data_prov_glo_itf + + "When the class is executed using F9, the object used here refers to type lcl_data_prov_glo_itf. + "When the unit test is executed, the object used here refers to type ltd_test_data_global_intf, + "i.e. the local test double is injected. + DATA(flight_data) = data_provider_global_itf->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_global_itf = total_seatsmax_global_itf + -seatsmax. + total_seatsocc_global_itf = total_seatsocc_global_itf + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_global_itf / total_seatsmax_global_itf * 100. + +ENDMETHOD. + +METHOD select_flight_data. +"Method that is identified as DOC in the method implementations above. +"This method is also used for demonstrating test double injection and method redefinition. + + SELECT seatsmax, seatsocc + FROM zdemo_abap_fli + WHERE carrid = @carrier + INTO CORRESPONDING FIELDS OF TABLE @flight_data. +ENDMETHOD. + +METHOD get_occ_rate_using_meth. + "This method demonstrates test double injection using inheritance and method redefinition. + + DATA total_seatsmax_no TYPE i. + DATA total_seatsocc_no TYPE i. + + "During the unit test, the redefined method in the test class is called. + DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_no = total_seatsmax_no + -seatsmax. + total_seatsocc_no = total_seatsocc_no + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_no / total_seatsmax_no * 100. + +ENDMETHOD. + +METHOD get_occ_rate_setter_inj. + "This method demonstrates test double injection using setting injection. + "See the setter_meth method. + + DATA total_seatsmax_setter_inj TYPE i. + DATA total_seatsocc_setter_inj TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing, a global interface + "is provided. + "In the example, an interface method is implemented in a local class in the local types + "tab (CCIMP include): lcl_data_prov_glo_itf + + "See the comment in the setter_meth method + DATA(flight_data) = data_provider_setter_inj->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_setter_inj = total_seatsmax_setter_inj + -seatsmax. + total_seatsocc_setter_inj = total_seatsocc_setter_inj + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_setter_inj / total_seatsmax_setter_inj * 100. + +ENDMETHOD. + +METHOD setter_meth. + "Method to demonstrate the test double injection using setter injection + + "When the unit test is executed, an object of the test double class is passed as + "a parameter. Then, the object used here refers to type ltd_test_data_setter_inj, + "i.e. the local test double is injected. + data_provider_setter_inj = data_prov. +ENDMETHOD. + +METHOD get_occ_rate_param_inj. + "This method demonstrates test double injection using parameter injection. + + DATA total_seatsmax_param_inj TYPE i. + DATA total_seatsocc_param_inj TYPE i. + + "Assumption: The original code in this method was as follows (the line commented out). + "It was identified as DOC (reading data from a database table). + + "DATA(flight_data) = select_flight_data( carrier = carrier_id ). + + "Instead of a method call like above and for a proper unit testing, a global interface + "is provided. + "In the example, an interface method is implemented in a local class in the local types + "tab (CCIMP include): lcl_data_prov_glo_itf + + "The method has an optional importing parameter. When the unit test is executed, + "the parameter is bound. An object of the test double class is passed in that case. + "Otherwise, when the class is executed using F9, an object of the actual data provider + "is created. + IF data_prov IS BOUND. + data_provider_param_inj = data_prov. + ELSE. + data_provider_param_inj = NEW lcl_data_prov_glo_itf( ). + ENDIF. + + DATA(flight_data) = data_provider_param_inj->select_flight_data( carrier = carrier_id ). + + LOOP AT flight_data ASSIGNING FIELD-SYMBOL(). + + total_seatsmax_param_inj = total_seatsmax_param_inj + -seatsmax. + total_seatsocc_param_inj = total_seatsocc_param_inj + -seatsocc. + + ENDLOOP. + + occupancy_rate = total_seatsocc_param_inj / total_seatsmax_param_inj * 100. + +ENDMETHOD. + +ENDCLASS. \ No newline at end of file diff --git a/src/zcl_demo_abap_unit_test.clas.testclasses.abap b/src/zcl_demo_abap_unit_test.clas.testclasses.abap index 608e2cf..cef5510 100644 --- a/src/zcl_demo_abap_unit_test.clas.testclasses.abap +++ b/src/zcl_demo_abap_unit_test.clas.testclasses.abap @@ -1,5 +1,5 @@ *********************************************************************** -* Test class overview +* Test Class Overview * * Note: * - This test include contains multiple test classes and methods @@ -21,7 +21,7 @@ * no dependent-on component (DOC). * * - ltc_test_simple_2 -* - Testing mutliple simple methods, no DOCs +* - Testing multiple simple methods, no DOCs * - Special methods setup and teardown * *- ltc_test_dummy @@ -35,20 +35,32 @@ *- ltc_test_doc_global_itf * - Testing one method for which a DOC has been identified and for which a global * interface exists -* - A Local test double class is included -* - Uses constructor injection as injection mechanism +* - A local test double class is included +* - Demonstrates constructor injection as injection mechanism * *- ltc_test_local_itf * - Testing one method for which a DOC has been identified * - There is no global interface available. Instead, a local interface is created. -* - A Local test double class is included -* - Uses back door injection as injection mechanism +* - A local test double class is included +* - Demonstrates back door injection as injection mechanism * *- ltc_test_redef -* - Testing one method for which a DOC has been identified - +* - Testing one method for which a DOC has been identified * - A local test double class is created by redefining a method of * the class under test. * +*- ltc_test_doc_setter_inj +* - Testing one method for which a DOC has been identified and for which a global +* interface exists +* - A local test double class is included +* - Demonstrates setter injection as injection mechanism +* +*- ltc_test_doc_param_inj +* - Testing one method for which a DOC has been identified and for which a global +* interface exists +* - A local test double class is included +* - Demonstrates parameter injection as injection mechanism +* *********************************************************************** "In this example, multiple test classes are created in the test include. @@ -64,13 +76,17 @@ CLASS ltc_test_doc_seam DEFINITION DEFERRED. CLASS ltc_test_doc_global_itf DEFINITION DEFERRED. CLASS ltc_test_doc_local_itf DEFINITION DEFERRED. CLASS ltc_test_doc_redef DEFINITION DEFERRED. +CLASS ltc_test_doc_setter_inj DEFINITION DEFERRED. +CLASS ltc_test_doc_param_inj DEFINITION DEFERRED. CLASS zcl_demo_abap_unit_test DEFINITION LOCAL FRIENDS ltc_test_simple_1 - ltc_test_simple_2 - ltc_test_doc_seam - ltc_test_doc_global_itf - ltc_test_doc_local_itf - ltc_test_doc_redef. + ltc_test_simple_2 + ltc_test_doc_seam + ltc_test_doc_global_itf + ltc_test_doc_local_itf + ltc_test_doc_redef + ltc_test_doc_setter_inj + ltc_test_doc_param_inj. *********************************************************************** * Test class ltc_test_simple_1 @@ -86,55 +102,55 @@ CLASS zcl_demo_abap_unit_test DEFINITION LOCAL FRIENDS ltc_test_simple_1 * demonstrate the errors in the ABAP Unit tab. *********************************************************************** CLASS ltc_test_simple_1 DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. +PRIVATE SECTION. - METHODS: test_get_digit_sum FOR TESTING. + METHODS: test_get_digit_sum FOR TESTING. ENDCLASS. CLASS ltc_test_simple_1 IMPLEMENTATION. - METHOD test_get_digit_sum. - "The method to be tested calculates the digit sum of an number. +METHOD test_get_digit_sum. + "The method to be tested calculates the digit sum of an number. - "Creating an object of class under test - DATA(ref_cut) = NEW zcl_demo_abap_unit_test( ). + "Creating an object of class under test + DATA(ref_cut) = NEW zcl_demo_abap_unit_test( ). - "Filling an internal table and providing some values against which the production - "method is tested a few times. There are values included for which the assertion - "deliberately fails. - "num1 = actual parameter for the method call - "num2 = expected result of the method call - DATA(tab_digit_sum) = VALUE zcl_demo_abap_unit_test=>nums_tab( ( num1 = 124 num2 = 7 ) - ( num1 = 57 num2 = 12 ) - ( num1 = 11111 num2 = 5 ) - ( num1 = 1000 num2 = 1 ) - ( num1 = 0 num2 = 0 ) + "Filling an internal table and providing some values against which the production + "method is tested a few times. There are values included for which the assertion + "deliberately fails. + "num1 = actual parameter for the method call + "num2 = expected result of the method call + DATA(tab_digit_sum) = VALUE zcl_demo_abap_unit_test=>nums_tab( ( num1 = 124 num2 = 7 ) + ( num1 = 57 num2 = 12 ) + ( num1 = 11111 num2 = 5 ) + ( num1 = 1000 num2 = 1 ) + ( num1 = 0 num2 = 0 ) - "To fail: - ( num1 = 124 num2 = 8 ) "correct: 7 - ( num1 = 57 num2 = 13 ) "correct: 12 - ( num1 = 11111 num2 = 6 ) ). "correct: 5 + "To fail: + ( num1 = 124 num2 = 8 ) "correct: 7 + ( num1 = 57 num2 = 13 ) "correct: 12 + ( num1 = 11111 num2 = 6 ) ). "correct: 5 - "Looping across the internal table and test the method with the values provided. - LOOP AT tab_digit_sum ASSIGNING FIELD-SYMBOL(). + "Looping across the internal table and test the method with the values provided. + LOOP AT tab_digit_sum ASSIGNING FIELD-SYMBOL(). - "Calling method that is to be tested - DATA(digit_sum) = ref_cut->get_digit_sum( -num1 ). + "Calling method that is to be tested + DATA(digit_sum) = ref_cut->get_digit_sum( -num1 ). - "Assertion - cl_abap_unit_assert=>assert_equals( - act = digit_sum - exp = -num2 - msg = |The digit sum of { -num1 } is not { -num2 }. It is { digit_sum }.| - quit = if_abap_unit_constant=>quit-no ). + "Assertion + cl_abap_unit_assert=>assert_equals( + act = digit_sum + exp = -num2 + msg = |The digit sum of { -num1 } is not { -num2 }. It is { digit_sum }.| + quit = if_abap_unit_constant=>quit-no ). - ENDLOOP. + ENDLOOP. - ENDMETHOD. +ENDMETHOD. ENDCLASS. @@ -164,206 +180,206 @@ ENDCLASS. * to run unit tests with RISK LEVEL DANGEROUS. *********************************************************************** CLASS ltc_test_simple_2 DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. +PRIVATE SECTION. - TYPES: BEGIN OF nums, - num1 TYPE i, - num2 TYPE i, - common_div TYPE zcl_demo_abap_unit_test=>int_tab_so, - gcd TYPE i, - END OF nums, - tab_type TYPE TABLE OF nums WITH EMPTY KEY. + TYPES: BEGIN OF nums, + num1 TYPE i, + num2 TYPE i, + common_div TYPE zcl_demo_abap_unit_test=>int_tab_so, + gcd TYPE i, + END OF nums, + tab_type TYPE TABLE OF nums WITH EMPTY KEY. - DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test. - METHODS: test_get_common_div_gcd_ok FOR TESTING, - test_get_common_div_gcd_fail FOR TESTING, + METHODS: test_get_common_div_gcd_ok FOR TESTING, + test_get_common_div_gcd_fail FOR TESTING, - test_get_digit_sum_ok FOR TESTING, - test_get_digit_sum_fail FOR TESTING, + test_get_digit_sum_ok FOR TESTING, + test_get_digit_sum_fail FOR TESTING, - test_get_sum_ok FOR TESTING, - test_get_sum_fail FOR TESTING, + test_get_sum_ok FOR TESTING, + test_get_sum_fail FOR TESTING, - setup, - teardown. + setup, + teardown. ENDCLASS. CLASS ltc_test_simple_2 IMPLEMENTATION. - METHOD setup. +METHOD setup. - "Creating an object of the class under test - ref_cut = NEW #( ). + "Creating an object of the class under test + ref_cut = NEW #( ). - "Preparing demo database table for testing the get_sum method - DELETE FROM zdemo_abap_tab1 - WHERE key_field = 987654321 - AND char1 = '@'. + "Preparing demo database table for testing the get_sum method + DELETE FROM zdemo_abap_tab1 + WHERE key_field = 987654321 + AND char1 = '@'. - MODIFY zdemo_abap_tab1 FROM @( - VALUE #( key_field = 987654321 - char1 = '@' - num1 = 444 - num2 = 555 ) ). + MODIFY zdemo_abap_tab1 FROM @( + VALUE #( key_field = 987654321 + char1 = '@' + num1 = 444 + num2 = 555 ) ). - ENDMETHOD. +ENDMETHOD. - METHOD teardown. - "Removing test data inserted into the demo database table for testing the get_sum method - DELETE FROM zdemo_abap_tab1 - WHERE key_field = 987654321 - AND char1 = '@'. - ENDMETHOD. +METHOD teardown. + "Removing test data inserted into the demo database table for testing the get_sum method + DELETE FROM zdemo_abap_tab1 + WHERE key_field = 987654321 + AND char1 = '@'. +ENDMETHOD. - METHOD test_get_common_div_gcd_ok. - "The method to be tested calculates the common divisors of two numbers - "and the greatest common divisor. +METHOD test_get_common_div_gcd_ok. + "The method to be tested calculates the common divisors of two numbers + "and the greatest common divisor. - "Filling an internal table and providing some values against which the production - "method is tested a few times (using a loop). - "num1/num2 = numbers on which the calculation is based - "common_div = table containing the expected common divisors - "gcd = expected greatest common divisor - DATA(tab) = VALUE tab_type( - ( num1 = 6 num2 = 3 common_div = VALUE #( ( 1 ) ( 3 ) ) gcd = 3 ) - ( num1 = 24 num2 = 36 common_div = VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 6 ) ( 12 ) ) gcd = 12 ) - ( num1 = 5 num2 = 7 common_div = VALUE #( ( 1 ) ) gcd = 1 ) - ( num1 = 21 num2 = 35 common_div = VALUE #( ( 1 ) ( 7 ) ) gcd = 7 ) ). + "Filling an internal table and providing some values against which the production + "method is tested a few times (using a loop). + "num1/num2 = numbers on which the calculation is based + "common_div = table containing the expected common divisors + "gcd = expected greatest common divisor + DATA(tab) = VALUE tab_type( + ( num1 = 6 num2 = 3 common_div = VALUE #( ( 1 ) ( 3 ) ) gcd = 3 ) + ( num1 = 24 num2 = 36 common_div = VALUE #( ( 1 ) ( 2 ) ( 3 ) ( 4 ) ( 6 ) ( 12 ) ) gcd = 12 ) + ( num1 = 5 num2 = 7 common_div = VALUE #( ( 1 ) ) gcd = 1 ) + ( num1 = 21 num2 = 35 common_div = VALUE #( ( 1 ) ( 7 ) ) gcd = 7 ) ). - "Looping across the internal table and test the method with the values provided. - LOOP AT tab ASSIGNING FIELD-SYMBOL(). - - "Calling method that is to be tested - ref_cut->get_common_div_and_gcd( EXPORTING a = -num1 - b = -num2 - IMPORTING common_divisors = DATA(a) - gcd = DATA(b) ). - - "Assertion - cl_abap_unit_assert=>assert_equals( - act = VALUE nums( common_div = a - gcd = b ) - exp = VALUE nums( common_div = -common_div - gcd = -gcd ) - msg = |Wrong result for { -num1 } and { -num2 }.| - quit = if_abap_unit_constant=>quit-no ). - - ENDLOOP. - - ENDMETHOD. - - METHOD test_get_common_div_gcd_fail. - "This method intentionally includes values to make the unit test fail. - - DATA(num1) = 6. - DATA(num2) = 3. - DATA(c_div) = VALUE zcl_demo_abap_unit_test=>int_tab_st( ( 1 ) ( 2 ) ). "correct: 1, 3 - DATA(g_c_d) = 4. "correct: 3 + "Looping across the internal table and test the method with the values provided. + LOOP AT tab ASSIGNING FIELD-SYMBOL(). "Calling method that is to be tested - ref_cut->get_common_div_and_gcd( EXPORTING a = num1 - b = num2 - IMPORTING common_divisors = DATA(cd) - gcd = DATA(gr) ). + ref_cut->get_common_div_and_gcd( EXPORTING a = -num1 + b = -num2 + IMPORTING common_divisors = DATA(a) + gcd = DATA(b) ). "Assertion cl_abap_unit_assert=>assert_equals( - act = VALUE nums( common_div = cd - gcd = gr ) - exp = VALUE nums( common_div = c_div - gcd = g_c_d ) - msg = |Wrong result for { num1 } and { num2 }.| + act = VALUE nums( common_div = a + gcd = b ) + exp = VALUE nums( common_div = -common_div + gcd = -gcd ) + msg = |Wrong result for { -num1 } and { -num2 }.| quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. + ENDLOOP. - METHOD test_get_digit_sum_ok. - "The method to be tested calculates the digit sum of a number. +ENDMETHOD. + +METHOD test_get_common_div_gcd_fail. + "This method intentionally includes values to make the unit test fail. + + DATA(num1) = 6. + DATA(num2) = 3. + DATA(c_div) = VALUE zcl_demo_abap_unit_test=>int_tab_st( ( 1 ) ( 2 ) ). "correct: 1, 3 + DATA(g_c_d) = 4. "correct: 3 + + "Calling method that is to be tested + ref_cut->get_common_div_and_gcd( EXPORTING a = num1 + b = num2 + IMPORTING common_divisors = DATA(cd) + gcd = DATA(gr) ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = VALUE nums( common_div = cd + gcd = gr ) + exp = VALUE nums( common_div = c_div + gcd = g_c_d ) + msg = |Wrong result for { num1 } and { num2 }.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_digit_sum_ok. + "The method to be tested calculates the digit sum of a number. - "Filling an internal table and providing some values against which the production - "method is tested a few times (using a loop). - "num1: Number on which the digit sum is calculated - "num2: Expected result - DATA(tab_digit_sum) = VALUE zcl_demo_abap_unit_test=>nums_tab( ( num1 = 124 num2 = 7 ) - ( num1 = 57 num2 = 12 ) - ( num1 = 11111 num2 = 5 ) - ( num1 = 1000 num2 = 1 ) - ( num1 = 0 num2 = 0 ) ). + "Filling an internal table and providing some values against which the production + "method is tested a few times (using a loop). + "num1: Number on which the digit sum is calculated + "num2: Expected result + DATA(tab_digit_sum) = VALUE zcl_demo_abap_unit_test=>nums_tab( ( num1 = 124 num2 = 7 ) + ( num1 = 57 num2 = 12 ) + ( num1 = 11111 num2 = 5 ) + ( num1 = 1000 num2 = 1 ) + ( num1 = 0 num2 = 0 ) ). - "Looping across the internal table and test the method with the values provided. - LOOP AT tab_digit_sum ASSIGNING FIELD-SYMBOL(). - - "Calling method that is to be tested - DATA(digit_sum) = ref_cut->get_digit_sum( -num1 ). - - "Assertion - cl_abap_unit_assert=>assert_equals( - act = digit_sum - exp = -num2 - msg = |The digit sum of { -num1 } is not { digit_sum }.| - quit = if_abap_unit_constant=>quit-no ). - - ENDLOOP. - - ENDMETHOD. - - METHOD test_get_digit_sum_fail. - "This method intentionally includes values to make the unit test fail. - - DATA(num) = 123. - DATA(dsum) = 7. "correct: 6 + "Looping across the internal table and test the method with the values provided. + LOOP AT tab_digit_sum ASSIGNING FIELD-SYMBOL(). "Calling method that is to be tested - DATA(digit_sum) = ref_cut->get_digit_sum( num ). + DATA(digit_sum) = ref_cut->get_digit_sum( -num1 ). "Assertion cl_abap_unit_assert=>assert_equals( act = digit_sum - exp = dsum - msg = |The digit sum of { num } is not { dsum }.| + exp = -num2 + msg = |The digit sum of { -num1 } is not { digit_sum }.| quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. + ENDLOOP. - METHOD test_get_sum_ok. - "The method to be tested calculates the sum of two numbers. - "Note: The setup method has prepared data in the database table. - "See the comments above. +ENDMETHOD. - DATA(exp_sum) = 999. +METHOD test_get_digit_sum_fail. + "This method intentionally includes values to make the unit test fail. - "Calling method that is to be tested - DATA(act_sum) = ref_cut->get_sum( key = 987654321 char = '@' ). + DATA(num) = 123. + DATA(dsum) = 7. "correct: 6 - "Assertion - cl_abap_unit_assert=>assert_equals( - act = act_sum - exp = exp_sum - msg = |The expected sum { exp_sum } is wrong.| + "Calling method that is to be tested + DATA(digit_sum) = ref_cut->get_digit_sum( num ). + + "Assertion + cl_abap_unit_assert=>assert_equals( + act = digit_sum + exp = dsum + msg = |The digit sum of { num } is not { dsum }.| quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_sum_fail. - "This method intentionally includes values to make the unit test fail. +METHOD test_get_sum_ok. + "The method to be tested calculates the sum of two numbers. + "Note: The setup method has prepared data in the database table. + "See the comments above. - DATA(exp_sum) = 998. "correct: 999 - DATA(act_sum) = ref_cut->get_sum( key = 987654321 char = '@' ). + DATA(exp_sum) = 999. - cl_abap_unit_assert=>assert_equals( - act = act_sum - exp = exp_sum - msg = |The expected sum { exp_sum } is wrong.| - quit = if_abap_unit_constant=>quit-no ). + "Calling method that is to be tested + DATA(act_sum) = ref_cut->get_sum( key = 987654321 char = '@' ). - ENDMETHOD. + "Assertion + cl_abap_unit_assert=>assert_equals( + act = act_sum + exp = exp_sum + msg = |The expected sum { exp_sum } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_sum_fail. + "This method intentionally includes values to make the unit test fail. + + DATA(exp_sum) = 998. "correct: 999 + DATA(act_sum) = ref_cut->get_sum( key = 987654321 char = '@' ). + + cl_abap_unit_assert=>assert_equals( + act = act_sum + exp = exp_sum + msg = |The expected sum { exp_sum } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. ENDCLASS. @@ -378,107 +394,107 @@ ENDCLASS. * test class is not made a local friend of the class under test. *********************************************************************** CLASS ltc_test_dummy DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. +PRIVATE SECTION. - METHODS: test_multiple_asserts_ok FOR TESTING, - test_multiple_asserts_fail FOR TESTING. + METHODS: test_multiple_asserts_ok FOR TESTING, + test_multiple_asserts_fail FOR TESTING. ENDCLASS. CLASS ltc_test_dummy IMPLEMENTATION. - METHOD test_multiple_asserts_ok. - "This method does not test a method of the class under test. +METHOD test_multiple_asserts_ok. + "This method does not test a method of the class under test. - DATA(ref) = NEW zcl_demo_abap_unit_test( ). + DATA(ref) = NEW zcl_demo_abap_unit_test( ). - DATA(is_true) = cl_abap_unit_assert=>assert_equals( - act = 1 - exp = 1 - msg = |assert_equals: Issue.| - quit = if_abap_unit_constant=>quit-no ). + DATA(is_true) = cl_abap_unit_assert=>assert_equals( + act = 1 + exp = 1 + msg = |assert_equals: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_bound( - act = ref - msg = |assert_bound: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_bound( + act = ref + msg = |assert_bound: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_differs( - act = `hallo` - exp = `hallo!` - msg = |assert_differs: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_differs( + act = `hallo` + exp = `hallo!` + msg = |assert_differs: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_initial( - act = 0 - msg = |assert_initial: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_initial( + act = 0 + msg = |assert_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_not_initial( - act = 1 - msg = |assert_not_initial: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_not_initial( + act = 1 + msg = |assert_not_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). - FIND `A` IN `ABAP`. + FIND `A` IN `ABAP`. - is_true = cl_abap_unit_assert=>assert_subrc( - exp = 0 - act = sy-subrc - msg = |assert_subrc: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_subrc( + exp = 0 + act = sy-subrc + msg = |assert_subrc: Issue.| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. - METHOD test_multiple_asserts_fail. - "This method intentionally includes values to make the unit test fail. +METHOD test_multiple_asserts_fail. + "This method intentionally includes values to make the unit test fail. - DATA ref TYPE REF TO zcl_demo_abap_unit_test. + DATA ref TYPE REF TO zcl_demo_abap_unit_test. - DATA(is_true) = cl_abap_unit_assert=>assert_equals( - exp = 'This a string that is checked.' - act = 'This a string that is checked ... but it is not the expected string.' - msg = |assert_equals: Issue.| - quit = if_abap_unit_constant=>quit-no ). + DATA(is_true) = cl_abap_unit_assert=>assert_equals( + exp = 'This a string that is checked.' + act = 'This a string that is checked ... but it is not the expected string.' + msg = |assert_equals: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_bound( - act = ref - msg = |assert_bound: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_bound( + act = ref + msg = |assert_bound: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_differs( - act = `hallo` - exp = `hallo` - msg = |assert_differs: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_differs( + act = `hallo` + exp = `hallo` + msg = |assert_differs: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_initial( - act = 1 - msg = |assert_initial: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_initial( + act = 1 + msg = |assert_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). - is_true = cl_abap_unit_assert=>assert_not_initial( - act = 0 - msg = |assert_not_initial: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_not_initial( + act = 0 + msg = |assert_not_initial: Issue.| + quit = if_abap_unit_constant=>quit-no ). - FIND `C` IN `ABAP`. + FIND `C` IN `ABAP`. - is_true = cl_abap_unit_assert=>assert_subrc( - exp = 0 - act = sy-subrc - msg = |assert_subrc: Issue.| - quit = if_abap_unit_constant=>quit-no ). + is_true = cl_abap_unit_assert=>assert_subrc( + exp = 0 + act = sy-subrc + msg = |assert_subrc: Issue.| + quit = if_abap_unit_constant=>quit-no ). - IF 1 <> 2. - cl_abap_unit_assert=>fail( - msg = |fail: Issue.| - quit = if_abap_unit_constant=>quit-no ). - ENDIF. + IF 1 <> 2. + cl_abap_unit_assert=>fail( + msg = |fail: Issue.| + quit = if_abap_unit_constant=>quit-no ). + ENDIF. - ENDMETHOD. +ENDMETHOD. ENDCLASS. @@ -495,158 +511,159 @@ ENDCLASS. * test seam injections. *********************************************************************** CLASS ltc_test_doc_seam DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. +PRIVATE SECTION. - DATA ref_cut TYPE REF TO zcl_demo_abap_unit_test. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + carrier_id TYPE zdemo_abap_fli-carrid. - METHODS: test_get_occ_rate_seam_ok FOR TESTING, - test_get_occ_rate_seam_fail FOR TESTING, - setup. + METHODS: test_get_occ_rate_seam_ok FOR TESTING, + test_get_occ_rate_seam_fail FOR TESTING, + setup. ENDCLASS. CLASS ltc_test_doc_seam IMPLEMENTATION. - METHOD setup. +METHOD setup. - "Creating an object of the class under test - ref_cut = NEW #( ). + "Creating an object of the class under test + ref_cut = NEW #( ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_seam_ok. - "The method to be tested calculates the occupancy rate of flights +METHOD test_get_occ_rate_seam_ok. + "The method to be tested calculates the occupancy rate of flights - "Creating test data - DATA carrier_id TYPE zdemo_abap_fli-carrid VALUE 'AB'. - DATA(expected_occupancy_rate) = '50.00'. + "Creating test data + carrier_id = 'AB'. + DATA(expected_occupancy_rate) = '50.00'. - "Injecting test seam into production code by replacing the code that is - "contained in the TEST-SEAM ... TEST-SEAM-END block - TEST-INJECTION select_flights. - seats_table = VALUE #( - ( carrid = carrier_id seatsmax = 100 seatsocc = 80 ) - ( carrid = carrier_id seatsmax = 100 seatsocc = 20 ) - ( carrid = carrier_id seatsmax = 200 seatsocc = 100 ) ). - END-TEST-INJECTION. + "Injecting test seam into production code by replacing the code that is + "contained in the TEST-SEAM ... TEST-SEAM-END block + TEST-INJECTION select_flights. + seats_table = VALUE #( + ( carrid = carrier_id seatsmax = 100 seatsocc = 80 ) + ( carrid = carrier_id seatsmax = 100 seatsocc = 20 ) + ( carrid = carrier_id seatsmax = 200 seatsocc = 100 ) ). + END-TEST-INJECTION. - "Further test seam injections - "The following example demonstrates an empty test seam in the production - "code. During the unit test run, the following code is injected. - TEST-INJECTION num1. - var = 1. - END-TEST-INJECTION. + "Further test seam injections + "The following example demonstrates an empty test seam in the production + "code. During the unit test run, the following code is injected. + TEST-INJECTION num1. + var = 1. + END-TEST-INJECTION. - "empty injection + "empty injection - "The following example demonstrates an empty test seam injection. - "There is code in a TEST-SEAM ... END-TEST-SEAM block in the - "production code. This code is replaced, and nothing is included - "for the test, i.e. during the unit test 'unwanted' code in the production - "code is ignored. - TEST-INJECTION num2. - END-TEST-INJECTION. + "The following example demonstrates an empty test seam injection. + "There is code in a TEST-SEAM ... END-TEST-SEAM block in the + "production code. This code is replaced, and nothing is included + "for the test, i.e. during the unit test 'unwanted' code in the production + "code is ignored. + TEST-INJECTION num2. + END-TEST-INJECTION. - "Calling method that is to be tested - ref_cut->get_occ_rate_test_seam( EXPORTING carrier_id = carrier_id - IMPORTING occupancy_rate = DATA(occupancy_rate) - num1 = DATA(num1) - num2 = DATA(num2) ). + "Calling method that is to be tested + ref_cut->get_occ_rate_test_seam( EXPORTING carrier_id = carrier_id + IMPORTING occupancy_rate = DATA(occupancy_rate) + num1 = DATA(num1) + num2 = DATA(num2) ). - "Assertions - cl_abap_unit_assert=>assert_equals( - act = occupancy_rate - exp = expected_occupancy_rate - msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| - quit = if_abap_unit_constant=>quit-no ). + "Assertions + cl_abap_unit_assert=>assert_equals( + act = occupancy_rate + exp = expected_occupancy_rate + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - cl_abap_unit_assert=>assert_equals( - act = num1 - exp = 999 - msg = |num1: { num1 }| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = num1 + exp = 999 + msg = |num1: { num1 }| + quit = if_abap_unit_constant=>quit-no ). - cl_abap_unit_assert=>assert_equals( - act = num2 - exp = 0 - msg = |num2: { num2 }| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = num2 + exp = 0 + msg = |num2: { num2 }| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_seam_fail. - "This method intentionally includes values to make the unit test fail. +METHOD test_get_occ_rate_seam_fail. + "This method intentionally includes values to make the unit test fail. - "Creating test data - DATA carrier_id TYPE zdemo_abap_fli-carrid VALUE 'CD'. - DATA(expected_occupancy_rate) = '60.00'. + "Creating test data + carrier_id = 'CD'. + DATA(expected_occupancy_rate) = '60.00'. - "Code injection - TEST-INJECTION select_flights. - seats_table = VALUE #( - ( carrid = carrier_id seatsmax = 100 seatsocc = 50 ) - ( carrid = carrier_id seatsmax = 200 seatsocc = 100 ) - ( carrid = carrier_id seatsmax = 300 seatsocc = 100 ) ). - END-TEST-INJECTION. + "Code injection + TEST-INJECTION select_flights. + seats_table = VALUE #( + ( carrid = carrier_id seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier_id seatsmax = 200 seatsocc = 100 ) + ( carrid = carrier_id seatsmax = 300 seatsocc = 100 ) ). + END-TEST-INJECTION. - TEST-INJECTION num1. - var = 1. - END-TEST-INJECTION. + TEST-INJECTION num1. + var = 1. + END-TEST-INJECTION. - TEST-INJECTION num2. - END-TEST-INJECTION. + TEST-INJECTION num2. + END-TEST-INJECTION. - "Calling method that is to be tested - ref_cut->get_occ_rate_test_seam( EXPORTING carrier_id = carrier_id - IMPORTING occupancy_rate = DATA(occupancy_rate) - num1 = DATA(num1) - num2 = DATA(num2) ). + "Calling method that is to be tested + ref_cut->get_occ_rate_test_seam( EXPORTING carrier_id = carrier_id + IMPORTING occupancy_rate = DATA(occupancy_rate) + num1 = DATA(num1) + num2 = DATA(num2) ). - "Assertions - cl_abap_unit_assert=>assert_equals( - act = occupancy_rate - exp = expected_occupancy_rate - msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| - quit = if_abap_unit_constant=>quit-no ). + "Assertions + cl_abap_unit_assert=>assert_equals( + act = occupancy_rate + exp = expected_occupancy_rate + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - cl_abap_unit_assert=>assert_equals( - act = num1 - exp = 1 - msg = |num1: { num1 }| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = num1 + exp = 1 + msg = |num1: { num1 }| + quit = if_abap_unit_constant=>quit-no ). - cl_abap_unit_assert=>assert_equals( - act = num2 - exp = 123 - msg = |num2: { num2 }| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = num2 + exp = 123 + msg = |num2: { num2 }| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. ENDCLASS. *********************************************************************** * Test class ltc_test_doc_global_itf * -* - Tests one method of the global class; uses constructor injection +* - Tests one method of the global class; demonstrates constructor injection * - In this case, a dependent-on component (DOC) has been identified (a * database access). -* - A global interface exists to overcome the DOC. +* - It is assumed that there is a global interface to overcome the DOC. * - A local test double class is created. It implements the global * interface. The method implementation contains manually created test * data. * -* Notes on constructor injection: +* Notes on constructor injection in this example: * - This means that a test double is passed as a parameter to the instance * constructor of the class under test. * - An interface reference variable is declared in the private section of * the class under test, and its type references the global interface. * - A local test class is created here for the test double. It implements -* the interface method needed for the test. Note the PARTIALLY IMPLEMENTED +* the interface method required by the test. Note the PARTIALLY IMPLEMENTED * addition to the interface. * - In this method implementation, local test data are created. * - The global class/class under test has the following instance constructor @@ -665,29 +682,29 @@ ENDCLASS. * Local test double class *********************************************************************** CLASS ltd_test_data_global_intf DEFINITION FOR TESTING. - PUBLIC SECTION. +PUBLIC SECTION. - "Note: Usually, you must implement all non-optional methods of interfaces. - "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. - INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. + "Note: Usually, you must implement all non-optional methods of interfaces. + "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. + INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. ENDCLASS. CLASS ltd_test_data_global_intf IMPLEMENTATION. - METHOD zdemo_abap_get_data_itf~select_flight_data. +METHOD zdemo_abap_get_data_itf~select_flight_data. - CLEAR flight_data. + CLEAR flight_data. - "Providing test data - flight_data = SWITCH #( carrier - WHEN 'EF' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) - ( carrid = carrier seatsmax = 200 seatsocc = 150 ) - ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) - WHEN 'GH' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) - ( carrid = carrier seatsmax = 350 seatsocc = 250 ) - ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'EF' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'GH' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). - ENDMETHOD. +ENDMETHOD. ENDCLASS. @@ -695,100 +712,106 @@ ENDCLASS. * Test class ltc_test_doc_global_itf *********************************************************************** CLASS ltc_test_doc_global_itf DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. - DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, - ref_data_prov TYPE REF TO zdemo_abap_get_data_itf. +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO zdemo_abap_get_data_itf, + carrier_id TYPE zdemo_abap_fli-carrid. - METHODS: setup, - test_get_occ_rate_glo_if_ok FOR TESTING, - test_get_occ_rate_glo_if_fail FOR TESTING. + METHODS: setup, + test_get_occ_rate_glo_if_ok FOR TESTING, + test_get_occ_rate_glo_if_fail FOR TESTING. ENDCLASS. CLASS ltc_test_doc_global_itf IMPLEMENTATION. - METHOD setup. +METHOD setup. - "Creating an instance of the local test double - ref_data_prov = NEW ltd_test_data_global_intf( ). + "Creating an instance of the local test double + ref_data_prov = NEW ltd_test_data_global_intf( ). - "Instance is provided for the constructor injection - ref_cut = NEW #( ref_data_prov ). + "Instance is provided for the constructor injection + ref_cut = NEW #( ref_data_prov ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_glo_if_ok. - "The method to be tested calculates the occupancy rate of flights. +METHOD test_get_occ_rate_glo_if_ok. + "The method to be tested calculates the occupancy rate of flights. - "(1) Calling method that is to be tested - "Due to constructor injection, the test double is used. - DATA(act_occ_rate) = ref_cut->get_occ_rate_global_itf( 'EF' ). + "(1) Calling method that is to be tested + "Due to constructor injection, the test double is used. + carrier_id = 'EF'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_global_itf( carrier_id ). - "Assertion - DATA(exp_value) = '50.00'. + "Assertion + DATA(exp_value) = '50.00'. - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier EF is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - "(2) Calling method that is to be tested - act_occ_rate = ref_cut->get_occ_rate_global_itf( 'GH' ). + "(2) Calling method that is to be tested + carrier_id = 'GH'. + act_occ_rate = ref_cut->get_occ_rate_global_itf( carrier_id ). - "Assertion - exp_value = '60.00'. + "Assertion + exp_value = '60.00'. - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier GH is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_glo_if_fail. - "This method intentionally includes values to make the unit test fail. +METHOD test_get_occ_rate_glo_if_fail. + "This method intentionally includes values to make the unit test fail. - "(1) Calling method that is to be tested - DATA(act_occ_rate) = ref_cut->get_occ_rate_global_itf( 'EF' ). + "(1) Calling method that is to be tested + carrier_id = 'EF'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_global_itf( carrier_id ). - "Assertion to fail - DATA(exp_value) = '40.00'. "correct: 50.00 + "Assertion to fail + DATA(exp_value) = '40.00'. "correct: 50.00 - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier EF is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - "(2) Calling method that is to be tested - act_occ_rate = ref_cut->get_occ_rate_global_itf( 'GH' ). + "(2) Calling method that is to be tested + carrier_id = 'GH'. + act_occ_rate = ref_cut->get_occ_rate_global_itf( carrier_id ). - "Assertion to fail - exp_value = '90.00'. "correct: 60.00 + "Assertion to fail + exp_value = '90.00'. "correct: 60.00 - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier GH is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. ENDCLASS. *********************************************************************** * Test class ltc_test_local_itf * -* - Tests one method of the global class; uses back door injection +* - Tests one method of the global class; demonstrates back door injection * - In this case, a dependent-on component (DOC) has been identified (a * database access). The method is similar to the one above. -* - There is no global interface to overcome the DOC. Instead, a local -* interface is created in the local types (CCIMP include): lif_get_data +* - It is assumed that there is no global interface to overcome the DOC. +* Instead, a local interface is created in the local types +* (CCIMP include): lif_get_data * - Additionally, a local class is implemented in the CCIMP include that * implements the local interface. This local class provides the data * for the method call in the global class (get_occ_rate_local_itf). @@ -798,7 +821,7 @@ ENDCLASS. * - In addition, the test class includes a helper method to demonstrate * the separation of code recurring tasks into separate methods. * -* Notes on back door injection: +* Notes on back door injection in this example: * - This means that a back door is created to inject a test double into * the class under test. * - This back door is implemented by granting friendship to the test @@ -816,25 +839,25 @@ ENDCLASS. * Local test double class *********************************************************************** CLASS ltd_test_data_local_itf DEFINITION FOR TESTING. - PUBLIC SECTION. - INTERFACES lif_get_data PARTIALLY IMPLEMENTED. +PUBLIC SECTION. + INTERFACES lif_get_data PARTIALLY IMPLEMENTED. ENDCLASS. CLASS ltd_test_data_local_itf IMPLEMENTATION. - METHOD lif_get_data~select_flight_data. +METHOD lif_get_data~select_flight_data. - CLEAR flight_data. + CLEAR flight_data. - "Providing test data - flight_data = SWITCH #( carrier - WHEN 'IJ' THEN VALUE #( ( carrid = carrier seatsmax = 300 seatsocc = 200 ) - ( carrid = carrier seatsmax = 350 seatsocc = 200 ) - ( carrid = carrier seatsmax = 350 seatsocc = 300 ) ) - WHEN 'KL' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 300 ) - ( carrid = carrier seatsmax = 350 seatsocc = 250 ) - ( carrid = carrier seatsmax = 300 seatsocc = 250 ) ) ). + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'IJ' THEN VALUE #( ( carrid = carrier seatsmax = 300 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 300 ) ) + WHEN 'KL' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 300 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 250 ) ) ). - ENDMETHOD. +ENDMETHOD. ENDCLASS. @@ -842,83 +865,84 @@ ENDCLASS. * Test class ltc_test_doc_local_itf *********************************************************************** CLASS ltc_test_doc_local_itf DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. +PRIVATE SECTION. - DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, - ref_data_prov TYPE REF TO lif_get_data. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO lif_get_data. - METHODS: setup, + METHODS: setup, - "Helper method - assert_occ_rate IMPORTING carrier_id TYPE zdemo_abap_fli-carrid - occ_rate TYPE lif_get_data=>occ_rate, + "Helper method + assert_occ_rate IMPORTING carrier_id TYPE zdemo_abap_fli-carrid + occ_rate TYPE lif_get_data=>occ_rate, - "Test methods - test_get_occ_rate_lo_itf_ok FOR TESTING, - test_get_occ_rate_lo_itf_fail FOR TESTING. + "Test methods + test_get_occ_rate_lo_itf_ok FOR TESTING, + test_get_occ_rate_lo_itf_fail FOR TESTING. ENDCLASS. CLASS ltc_test_doc_local_itf IMPLEMENTATION. - METHOD setup. +METHOD setup. - ref_cut = NEW #( ). + ref_cut = NEW #( ). - ref_data_prov = NEW ltd_test_data_local_itf( ). + ref_data_prov = NEW ltd_test_data_local_itf( ). - "Back door injection - ref_cut->data_provider_local_itf = ref_data_prov. + "Back door injection + ref_cut->data_provider_local_itf = ref_data_prov. - ENDMETHOD. +ENDMETHOD. - METHOD assert_occ_rate. +METHOD assert_occ_rate. - DATA(act_occ_rate) = ref_cut->get_occ_rate_local_itf( carrier_id ). + DATA(act_occ_rate) = ref_cut->get_occ_rate_local_itf( carrier_id ). - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = occ_rate - msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = occ_rate + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_lo_itf_ok. +METHOD test_get_occ_rate_lo_itf_ok. - assert_occ_rate( carrier_id = 'IJ' - occ_rate = '70.00' ). + assert_occ_rate( carrier_id = 'IJ' + occ_rate = '70.00' ). - assert_occ_rate( carrier_id = 'KL' - occ_rate = '80.00' ). + assert_occ_rate( carrier_id = 'KL' + occ_rate = '80.00' ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_lo_itf_fail. - "This method intentionally includes values to make the unit test fail. +METHOD test_get_occ_rate_lo_itf_fail. + "This method intentionally includes values to make the unit test fail. - assert_occ_rate( carrier_id = 'IJ' - occ_rate = '20.00' ). "correct: 70.00 + assert_occ_rate( carrier_id = 'IJ' + occ_rate = '20.00' ). "correct: 70.00 - assert_occ_rate( carrier_id = 'KL' - occ_rate = '30.00' ). "correct: 80.00 + assert_occ_rate( carrier_id = 'KL' + occ_rate = '30.00' ). "correct: 80.00 - ENDMETHOD. +ENDMETHOD. ENDCLASS. *********************************************************************** * Test class ltc_test_redef * -* - Tests one method of the global class; uses constructor injection +* - Tests one method of the global class; demonstrates constructor injection * - In this case, a dependent-on component (DOC) has been identified (a * database access). -* - There are no global and local interfaces to overcome the DOC. +* - It is assumed that there are no global and local interfaces to overcome +* the DOC. * - A local test double class is created by redefining a method of * the class under test. The method implementation contains manually * created test data. @@ -942,26 +966,26 @@ ENDCLASS. CLASS ltd_test_data_redef DEFINITION FOR TESTING INHERITING FROM zcl_demo_abap_unit_test. - PROTECTED SECTION. - METHODS select_flight_data REDEFINITION. +PROTECTED SECTION. + METHODS select_flight_data REDEFINITION. ENDCLASS. CLASS ltd_test_data_redef IMPLEMENTATION. - METHOD select_flight_data. +METHOD select_flight_data. - CLEAR flight_data. + CLEAR flight_data. - "Providing test data - flight_data = SWITCH #( carrier - WHEN 'UV' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) - ( carrid = carrier seatsmax = 200 seatsocc = 150 ) - ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) - WHEN 'WX' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) - ( carrid = carrier seatsmax = 350 seatsocc = 250 ) - ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'MN' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'OP' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). - ENDMETHOD. +ENDMETHOD. ENDCLASS. @@ -969,72 +993,392 @@ ENDCLASS. * Test class ltc_test_doc_redef *********************************************************************** CLASS ltc_test_doc_redef DEFINITION FOR TESTING - RISK LEVEL HARMLESS - DURATION SHORT. +RISK LEVEL HARMLESS +DURATION SHORT. - PRIVATE SECTION. - DATA ref_cut TYPE REF TO zcl_demo_abap_unit_test. +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + carrier_id TYPE zdemo_abap_fli-carrid. - METHODS: setup, - test_get_occ_rate_redef_ok FOR TESTING, - test_get_occ_rate_redef_fail FOR TESTING. + METHODS: setup, + test_get_occ_rate_redef_ok FOR TESTING, + test_get_occ_rate_redef_fail FOR TESTING. ENDCLASS. CLASS ltc_test_doc_redef IMPLEMENTATION. - METHOD setup. +METHOD setup. - ref_cut = NEW ltd_test_data_redef( ). + ref_cut = NEW ltd_test_data_redef( ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_redef_ok. +METHOD test_get_occ_rate_redef_ok. - DATA(act_occ_rate) = ref_cut->get_occ_rate_using_meth( 'UV' ). + carrier_id = 'MN'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_using_meth( carrier_id ). - DATA(exp_value) = '50.00'. + DATA(exp_value) = '50.00'. - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier UV is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - act_occ_rate = ref_cut->get_occ_rate_using_meth( 'WX' ). + carrier_id = 'OP'. + act_occ_rate = ref_cut->get_occ_rate_using_meth( carrier_id ). - exp_value = '60.00'. + exp_value = '60.00'. - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier WX is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. - METHOD test_get_occ_rate_redef_fail. +METHOD test_get_occ_rate_redef_fail. - DATA(act_occ_rate) = ref_cut->get_occ_rate_using_meth( 'UV' ). + carrier_id = 'MN'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_using_meth( carrier_id ). - DATA(exp_value) = '40.00'. "correct: 50.00 + DATA(exp_value) = '40.00'. "correct: 50.00 - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier UV is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - act_occ_rate = ref_cut->get_occ_rate_using_meth( 'WX' ). + carrier_id = 'OP'. + act_occ_rate = ref_cut->get_occ_rate_using_meth( carrier_id ). - exp_value = '90.00'. "correct: 60.00 + exp_value = '90.00'. "correct: 60.00 - cl_abap_unit_assert=>assert_equals( - act = act_occ_rate - exp = exp_value - msg = |The expected occupancy rate for carrier WX is wrong.| - quit = if_abap_unit_constant=>quit-no ). + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). - ENDMETHOD. +ENDMETHOD. ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_setter_inj +* +* - Tests one method of the global class; demonstrates setter injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). +* - It is assumed that there is a global interface to overcome the DOC. +* - A local test double class is created. It implements the global +* interface. The method implementation contains manually created test +* data. +* +* Notes on setter injection in this example: +* - This means that an object of the test double class is passed as a +* parameter to a setter method in the class under test. +* - An interface reference variable is declared in the private section of +* the class under test, and its type references the global interface. +* - A local test class is created here for the test double. It implements +* the interface method required by the test. Note the PARTIALLY IMPLEMENTED +* addition to the interface. +* - In this method implementation, local test data are created. +* - The global class/class under test contains a setter method (setter_meth) +* that has an importing parameter of type reference to the global interface. +* - When the unit test is executed (and only then), the setter method is +* called in the test class. Before the call is made, an object of the +* test double class is created. This object is passed to the setter method. +* - In the implementation of the setter method, the interface variable declared in +* the private section of the class under test is assigned the object of +* the test double class. When the method is 'usually' called +* (i.e., not in the context of a unit test), for example, when the +* class is run using F9, the instance constructor implementation involves +* creating an object of the actual data provider that is assigned to +* the interface variable. Therefore, when the unit test is executed, the +* assigned object is replaced by the setter method and the test double +* is used instead. +*********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_setter_inj DEFINITION FOR TESTING. +PUBLIC SECTION. + + "Note: Usually, you must implement all non-optional methods of interfaces. + "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. + INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. + +ENDCLASS. + +CLASS ltd_test_data_setter_inj IMPLEMENTATION. +METHOD zdemo_abap_get_data_itf~select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'QR' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'ST' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_setter_inj +*********************************************************************** +CLASS ltc_test_doc_setter_inj DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO zdemo_abap_get_data_itf, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: setup, + test_get_occ_rate_set_inj_ok FOR TESTING, + test_get_occ_rate_set_inj_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_doc_setter_inj IMPLEMENTATION. + +METHOD setup. + + ref_cut = NEW #( ). + + "Creating an instance of the local test double + ref_data_prov = NEW ltd_test_data_setter_inj( ). + + "Setter injection + "Passing the test double as a parameter of a setter method + ref_cut->setter_meth( ref_data_prov ). + +ENDMETHOD. + +METHOD test_get_occ_rate_set_inj_ok. + "The method to be tested calculates the occupancy rate of flights. + + "(1) Calling method that is to be tested + "Due to constructor injection, the test double is used. + carrier_id = 'QR'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion + DATA(exp_value) = '50.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'ST'. + act_occ_rate = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion + exp_value = '60.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_set_inj_fail. + "This method intentionally includes values to make the unit test fail. + + "(1) Calling method that is to be tested + carrier_id = 'QR'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion to fail + DATA(exp_value) = '40.00'. "correct: 50.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'ST'. + act_occ_rate = ref_cut->get_occ_rate_setter_inj( carrier_id ). + + "Assertion to fail + exp_value = '90.00'. "correct: 60.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_param_inj +* +* - Tests one method of the global class; demonstrates parameter injection +* - In this case, a dependent-on component (DOC) has been identified (a +* database access). +* - It is assumed that there is a global interface to overcome the DOC. +* - A local test double class is created. It implements the global +* interface. The method implementation contains manually created test +* data. +* +* Notes on parameter injection in this example: +* - This means that an object of the test double class is passed as a +* parameter of the tested method in the class under test. +* - This parameter is optional. When the unit test is run, the parameter +* is bound. An object of the test double class is passed in this case. +* Otherwise, when the method is 'usually' called (i.e., not in the +* context of a unit test), for example, when the class is run using F9, +* the parameter is not bound. Then, an object of the actual data +* provider is created. The method in the class under test contains a +* check in the implementation (IF ... IS BOUND ...). +*********************************************************************** + +*********************************************************************** +* Local test double class +*********************************************************************** +CLASS ltd_test_data_param_inj DEFINITION FOR TESTING. +PUBLIC SECTION. + + "Note: Usually, you must implement all non-optional methods of interfaces. + "Without the addition PARTIALLY IMPLEMENTED, there would be a syntax error. + INTERFACES zdemo_abap_get_data_itf PARTIALLY IMPLEMENTED. + +ENDCLASS. + +CLASS ltd_test_data_param_inj IMPLEMENTATION. +METHOD zdemo_abap_get_data_itf~select_flight_data. + + CLEAR flight_data. + + "Providing test data + flight_data = SWITCH #( carrier + WHEN 'UV' THEN VALUE #( ( carrid = carrier seatsmax = 100 seatsocc = 50 ) + ( carrid = carrier seatsmax = 200 seatsocc = 150 ) + ( carrid = carrier seatsmax = 300 seatsocc = 100 ) ) + WHEN 'WX' THEN VALUE #( ( carrid = carrier seatsmax = 350 seatsocc = 200 ) + ( carrid = carrier seatsmax = 350 seatsocc = 250 ) + ( carrid = carrier seatsmax = 300 seatsocc = 150 ) ) ). + +ENDMETHOD. + +ENDCLASS. + +*********************************************************************** +* Test class ltc_test_doc_param_inj +*********************************************************************** +CLASS ltc_test_doc_param_inj DEFINITION FOR TESTING +RISK LEVEL HARMLESS +DURATION SHORT. + +PRIVATE SECTION. + DATA: ref_cut TYPE REF TO zcl_demo_abap_unit_test, + ref_data_prov TYPE REF TO zdemo_abap_get_data_itf, + carrier_id TYPE zdemo_abap_fli-carrid. + + METHODS: setup, + test_get_occ_rate_par_inj_ok FOR TESTING, + test_get_occ_rate_par_inj_fail FOR TESTING. + +ENDCLASS. + +CLASS ltc_test_doc_param_inj IMPLEMENTATION. + +METHOD setup. + + ref_cut = NEW #( ). + + "Creating an instance of the local test double + ref_data_prov = NEW ltd_test_data_param_inj( ). + +ENDMETHOD. + +METHOD test_get_occ_rate_par_inj_ok. + "The method to be tested calculates the occupancy rate of flights. + + "(1) Calling method that is to be tested + "Due to constructor injection, the test double is used. + carrier_id = 'UV'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion + DATA(exp_value) = '50.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'WX'. + act_occ_rate = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion + exp_value = '60.00'. + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +METHOD test_get_occ_rate_par_inj_fail. + "This method intentionally includes values to make the unit test fail. + + "(1) Calling method that is to be tested + carrier_id = 'UV'. + DATA(act_occ_rate) = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion to fail + DATA(exp_value) = '40.00'. "correct: 50.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + + "(2) Calling method that is to be tested + carrier_id = 'WX'. + act_occ_rate = ref_cut->get_occ_rate_param_inj( carrier_id = carrier_id + data_prov = ref_data_prov ). + + "Assertion to fail + exp_value = '90.00'. "correct: 60.00 + + cl_abap_unit_assert=>assert_equals( + act = act_occ_rate + exp = exp_value + msg = |The expected occupancy rate for carrier { carrier_id } is wrong.| + quit = if_abap_unit_constant=>quit-no ). + +ENDMETHOD. + +ENDCLASS. \ No newline at end of file