Skip to Content
Technical Articles
Author's profile photo Neha Khandagade

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.

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.

Assigned Tags

      13 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Michelle Crapo
      Michelle Crapo

      Nice.  Pictures always add a nice touch.

      Michelle

      Author's profile photo Matthew Billingham
      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.

      Author's profile photo Michelle Crapo
      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.

      Author's profile photo Ila Chaudhary
      Ila Chaudhary

      Hi ,

      Nice work.

      Thanks

      Author's profile photo Kunal Jauhari
      Kunal Jauhari

      This provides a great insight on the ABAP Unit Test Classes.

      Great Work. Keep going.

      Author's profile photo Devansh Pandey
      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

      Author's profile photo Justin Loranger
      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

      Author's profile photo Samantak Chatterjee
      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.

      Author's profile photo Sandra Rossi
      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).

      Author's profile photo Alexandr Razinkin
      Alexandr Razinkin

      I also needed osql test double framework functions in 7.50, 7.40 and older systems. So I created my own custom library zsql_test_double_framework
      It also lets to test SQL statements but only if you run it with methods of the library. But it works from version 7.02.
      I have written documentation on github.
      Hope it will be useful.

      Author's profile photo Urvashi Tibrewal
      Urvashi Tibrewal

      Great Work!!

      Author's profile photo Subrahmanya Pindiproli
      Subrahmanya Pindiproli

      Very well articulated !!

       

      Author's profile photo Ravikant Sharma
      Ravikant Sharma

      Hi Neha,

      Done very well.

      However i had a doubt, can we also mock data for a DB view? if yes then how if no then what other solution except skipping the code?

       

      Regards

      Ravi