Wednesday 10 October 2018

Use of Test Environment in ABAP Unit Test Class

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.

1. 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:

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

Figure 1

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

Figure 2

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

Figure 3

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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_Demo2 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_Demo2
*?</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_Demo2 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.

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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.

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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.

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

Figure 10: Use of Test environment in UTC

2. Unit test case for else condition:

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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.

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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

SAP ABAP Tutorial and Material, SAP ABAP Guides, SAP ABAP Learning, SAP ABAP Study Materials

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.

No comments:

Post a Comment