Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
Introduction:

ABAP Unit Test class, a simpler way to verify the behavior of our code. The unit test class not only check the code coverage but helps a developer to cover all possible scenarios of a code leading to more reliable code with less chances of rework.

Content:

This blog will explain the following points:

  1. Generation of unit test class in SE80.

  2. OSQL test double framework.

  3. Unit test case method for each scenario.

  4. Use of Test-Seam and Test-Injection.



  • Generation of Unit Test Class in SE80:


For demo purpose, I have created one class ‘ZCL_UNIT_TEST_CLASS_DEMO1’ which is having one method ‘READ_BOM_VERSION’. There is a wizard through which we can generate a test class.

Following are the steps for generating test class:

 Figure 1


 


Figure 2


 


Figure 3


 


                         Figure 4 : Click on create button and provide test class name.                                         Select all options to generate predefined methods.


 


Figure 5 : Select the methods for which we want to generate test class.


 


Figure 6 : Test class generation is completed.


 

On completion of test class generation, local test class is generated( Figure 7), this we can modify based on our requirement.
class ltc_Unit_Test_Class_Demo1 definition for testing
duration short
risk level harmless.
*?<asx:abap xmlns:asx="http://www.sap.com/abapxml" version="1.0">
*?<asx:values>
*?<TESTCLASS_OPTIONS>
*?<TEST_CLASS>ltc_Unit_Test_Class_Demo1
*?</TEST_CLASS>
*?<TEST_MEMBER>f_Cut
*?</TEST_MEMBER>
*?<OBJECT_UNDER_TEST>ZCL_UNIT_TEST_CLASS_DEMO1
*?</OBJECT_UNDER_TEST>
*?<OBJECT_IS_LOCAL/>
*?<GENERATE_FIXTURE>X
*?</GENERATE_FIXTURE>
*?<GENERATE_CLASS_FIXTURE>X
*?</GENERATE_CLASS_FIXTURE>
*?<GENERATE_INVOCATION>X
*?</GENERATE_INVOCATION>
*?<GENERATE_ASSERT_EQUAL>X
*?</GENERATE_ASSERT_EQUAL>
*?</TESTCLASS_OPTIONS>
*?</asx:values>
*?</asx:abap>
private section.
data:
f_Cut type ref to zcl_Unit_Test_Class_Demo1. "class under test
class-methods: class_Setup.
class-methods: class_Teardown.
methods: setup.
methods: teardown.
methods: read_Bom_Version for testing.
endclass. "ltc_Unit_Test_Class_Demo2
class ltc_Unit_Test_Class_Demo1 implementation.
method class_Setup.
endmethod.
method class_Teardown.
endmethod.
method setup.
create object f_Cut.
endmethod.
method teardown.
endmethod.
method read_Bom_Version.
data is_Mass_Det type mpes_Majorassembly.
data is_Rcnip01 type rcnip01.
data ev_Bom_Ver type cs_Versn.
data et_Bom_Ver type zcl_Unit_Test_Class_Demo1=>tt_Bom_Ver.
f_Cut->read_Bom_Version(
EXPORTING
IS_MASS_DET = is_Mass_Det
IS_RCNIP01 = is_Rcnip01
* IMPORTING
* EV_BOM_VER = ev_Bom_Ver
* ET_BOM_VER = et_Bom_Ver
).
cl_Abap_Unit_Assert=>assert_Equals(
act = ev_Bom_Ver
exp = ev_Bom_Ver "<--- please adapt expected value
" msg = 'Testing value ev_Bom_Ver'
* level =
).
cl_Abap_Unit_Assert=>assert_Equals(
act = et_Bom_Ver
exp = et_Bom_Ver "<--- please adapt expected value
" msg = 'Testing value et_Bom_Ver'
* level =
).
endmethod.
endclass.

  Figure 7


 

  • OSQL test double framework:


This framework is very helpful for business logic testing without any database dependency. This framework is mainly used to cover select queries in production code without any impact on database. Through this, we create a virtual environment of select queries and then mocking data into it, to check the behavior of our code.

How it works?

  • First create a static attribute of type reference to interface ‘IF_OSQL_TEST_ENVIRONMENT’.

  • Creates an instance of Test Environment for test execution in predefined method ‘CLASS_SETUP’ and declare all the tables in it which were used in select queries of main class methods.

  • With this, we will be creating an environment for select queries in our methods and we will mock those tables in which data is coming from select query.



 Figure 8 : Test environment for DB artifact redirection


 

  • Unit test case method for each scenario:


Now, we will change unit test cases (which was generated while generating test class) for each method as per our requirement.

For example:  In method ‘READ_BOM_VERSION’, there are two possible scenarios, one is highlighted as red and the other one as yellow. At a time, only one scenario will be executed and hence for this method, two unit test cases will be needed.


 Figure 9 


 

  1. Unit test case for condition: if lv_line EQ 1:


Here, we are mocking all the input parameters and the tables of select query. And we are inserting that test data in dummy environment.


Figure 10: Use of Test environment in UTC


 

2. Unit test case for else condition:


Figure 11


 

  • Use of Test-Seam and Test-Injection:


In unit test class, we do not cover the lines of any ‘CALL FUNCTION’ and ‘CALL METHOD’ (of some other class). To skip this part, we use Test-Seam—End-Test-Seam in production code. For every Test-Seam—End-Test-Seam, there will be a corresponding Test-Injection—End-Test-Injection in unit test class.

By using Test-Seam and Test-Injection, we are avoiding the dependency of some another class or function module from our code.

If some output is expected from that call function, then we can mock it in the corresponding Test-Injection—End-Test-Injection.


Figure 12 : Use of Test-Seam--End-Test-Seam in class method.



Figure 13 : Use of Test-Injection--End-Test-Injection in UTC.


Earlier, we used to skip select queries in unit test class (to avoid database dependency) by using Test-Seam—End-Test-Seam, which leads to affect the code coverage. With OSQL environment, we create a virtual database, that not only allows us to check behavior of select queries but also help us to achieve better code coverage.

 

Below is a complete code of Unit Test Class :
*"* use this source file for your ABAP unit test classes
CLASS ltc_unit_test_class_demo1 DEFINITION DEFERRED.
CLASS zcl_unit_test_class_demo1 DEFINITION LOCAL FRIENDS ltc_unit_test_class_demo1.

CLASS ltc_unit_test_class_demo1 DEFINITION FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA: f_cut TYPE REF TO zcl_unit_test_class_demo1. "class under test
CLASS-DATA : environment TYPE REF TO if_osql_test_environment.
CLASS-METHODS: class_setup.
CLASS-METHODS: class_teardown.
METHODS: setup.
METHODS: teardown.
METHODS: read_bom_version_if FOR TESTING.
METHODS: read_bom_version_else FOR TESTING.
ENDCLASS. "ltc_Unit_Test_Class_Demo1


CLASS ltc_unit_test_class_demo1 IMPLEMENTATION.

METHOD class_setup.
environment = cl_osql_test_environment=>create( i_dependency_list = VALUE #( ( 'MAST' ) ( 'STZU' ) ( 'STKO' ) ) ).
ENDMETHOD.

METHOD class_teardown.
ENDMETHOD.

METHOD setup.
CREATE OBJECT f_cut.
ENDMETHOD.

METHOD teardown.
ENDMETHOD.

METHOD read_bom_version_if.
DATA: is_mass_det TYPE mpes_majorassembly,
is_rcnip01 TYPE rcnip01,
lv_bom_ver TYPE cs_versn ##NEEDED,
lt_bom_ver TYPE zcl_unit_test_class_demo1=>tt_bom_ver,
lt_mast_bm TYPE TABLE OF mast,
lt_stzu_bm TYPE TABLE OF stzu,
lt_stko_bm TYPE TABLE OF stko.
is_mass_det = VALUE #( matnr = 'MAJOR_ASMBLY03' plant = '0001' stlal = '01').
is_rcnip01 = VALUE #( stlan = 'V' ).
lt_mast_bm = VALUE #( ( matnr = 'MAJOR_ASMBLY03' werks = '0001'
stlnr = '89876' stlan = 'V' stlal = '01' ) ).
lt_stzu_bm = VALUE #( ( stlty = 'M' stlnr = '89876' versnind = 'X' ) ).
lt_stko_bm = VALUE #( ( stlty = 'M' stlnr = '89876' versnst = '99' bom_versn = '0001') ).
environment->insert_test_data( lt_mast_bm ).
environment->insert_test_data( lt_stzu_bm ).
environment->insert_test_data( lt_stko_bm ).
f_cut->read_bom_version(
EXPORTING
is_mass_det = is_mass_det
is_rcnip01 = is_rcnip01
IMPORTING
ev_bom_ver = lv_bom_ver
et_bom_ver = lt_bom_ver ).
"Checking the method
cl_abap_unit_assert=>assert_not_initial(
act = lt_bom_ver
msg = 'Testing Read BOM Version Failed' ).
ENDMETHOD.

METHOD read_bom_version_else.
DATA: is_mass_det TYPE mpes_majorassembly,
is_rcnip01 TYPE rcnip01,
lv_bom_ver TYPE cs_versn ##NEEDED,
lt_bom_ver TYPE zcl_unit_test_class_demo1=>tt_bom_ver,
lt_mast TYPE TABLE OF mast,
lt_stzu TYPE TABLE OF stzu,
lt_stko TYPE TABLE OF stko.
is_mass_det = VALUE #( matnr = 'MAJOR_ASMBLY1' plant = '0001' stlal = '01').

is_rcnip01 = VALUE #( stlan = 'V' ).

lt_mast = VALUE #( ( matnr = 'MAJOR_ASMBLY1' werks = '0001' stlnr = '89878' stlan = 'V' stlal = '01' )
( matnr = 'MAJOR_ASMBLY1' werks = '0001' stlnr = '234561' stlan = 'V' stlal = '01' ) ).

lt_stzu = VALUE #( ( stlty = 'M' stlnr = '89878' versnind = 'X' )
( stlty = 'M' stlnr = '234561' versnind = 'X' ) ).

lt_stko = VALUE #( ( stlty = 'M' stlnr = '89878' versnst = '01' bom_versn = '0001')
( stlty = 'M' stlnr = '234561' versnst = '99' bom_versn = '0002') ).

environment->insert_test_data( lt_mast ).
environment->insert_test_data( lt_stzu ).
environment->insert_test_data( lt_stko ).
f_cut->read_bom_version(
EXPORTING
is_mass_det = is_mass_det
is_rcnip01 = is_rcnip01
IMPORTING
ev_bom_ver = lv_bom_ver
et_bom_ver = lt_bom_ver ).
"Checking the method
cl_abap_unit_assert=>assert_not_initial(
act = lt_bom_ver
msg = 'Testing Read BOM Version Failed' ).
ENDMETHOD.
ENDCLASS.
13 Comments
Labels in this area