Skip to Content

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.
To report this post you need to login first.

9 Comments

You must be Logged on to comment or reply to a post.

    1. Matthew Billingham

      Yes. Nicely written.

      I knew about the wizard, but I’ve never used it. I probably won’t either as I use Eclipse almost exclusively now for development.

      Off topic, but three things finally sold Eclipse to me:

      1. Local revision history – in a session, each save generates a local version you can revert to if necessary

      2. The objects I was working on + the place I was at are retained. So I can stop at the end of the day, and pick up the next day from where I left of.

      3. Quick fixes.

      (5) 
      1. Michelle Crapo

        And opening many sessions at once. is an advantage too.  However, I like the debug on the GUI better than the eclipse version.  Maybe I’m not looking at it enough.

        (3) 
  1. Devansh Pandey

    Really Nice Blog .

    This method to create virtual database for select queries is very nice.

    Found it really helpful and productive.

    Thanks,

    Devansh Pandey

    (2) 
  2. Justin Loranger

    Thank you for the post.

    I agree with Devansh, of particular interest to me was the use of the OSQL test double framework.

    I have been trying to move my own development towards TDD and using Unit Testing as a matter of practice.  So, it is good to see other options which are available to implement unit tests.  I know there are other frameworks, but sometimes it takes a post and few pictures to kick yourself into gear and explore further.

    I would also agree with Matthew, using Eclipse for this style of development makes it so much easier.  I use Eclipse almost exclusively for my development, but I still do from time to time bounce back to the GUI for certain processes.

    Thank you,

    Justin Loranger

    (1) 
  3. Samantak Chatterjee

    Hi Neha,

    Thanks for writing this wonderful blog for the ABAP Unit Tests. Unfortunately, all the above methods of test doubles are available only in SAP NetWeaver 7.5 and higher version. For the lower versions we need to use the existing techniques of mocking. One of the very popular one is using the class CL_ABAP_TESTDOUBLE.

    Is there any chance that these will be down ported to the lower versions in the near future. I’m afraid that I won’t get a chance to get my hands dirty in ABAP 7.5 very soon.

     

    Thanks in advance.

    Best Regards,

    Samantak.

    (2) 
    1. Sandra Rossi

      ABAP Unit is available since 6.40. The Open SQL Test Double Framework is available since 7.52. The Test Seams are available since 7.50. CL_ABAP_TESTDOUBLE is available since 7.40 SP9 (before that, you may use MockA).

      (3) 

Leave a Reply