Skip to Content
Technical Articles

ABAP Unit – Learning session

Dear friends,

 

time is little bit long with the COVID, so I decide to make little videos. I really appreciate to do it, I try to improve my terrible English 🙂

 

The subject is the ABAP Unit.

ABAP Unit is a tool to help you to make Unit Test, meaning at the lowel level of the code. It is a developper tool, it will help you to strengthening your code.

 

  • Introduction

First video will try to put things in place. What you could do & what you could not do. (because most of the people think about functional test, but it is not)

It will present also, quickly, how to write simple test.

 

 

  • SETUP method & Pool of data

Second video will give you two new information. The usage of the method SETUP (simple example), and how to test several data at the same times.

 

 

 

  • Create a Mock & Test Exception

Third video will present you a solution to solve dependency: If you are trying to test a method & this method used another class. How to limit the test to the method itself & not test another class.

We will also test if a method is returning an exception

 

  • Test Drive Development overview

Fourth video will present you the principle of the TDD, with a simple example

 

 

 

I am searching good subjects for next videos.

I hope it will help peoples trying to start on ABAP Unit. If you need more details about a specific part of ABAP Unit, please add a comment.

 

Best regards

Fred

 

22 Comments
You must be Logged on to comment or reply to a post.
  • Frederic Girod

    Hi Fred,

    thanks for your blog post and the videos! I already watched the first and will soon watch the other two. I have one question: would it be possible to post the code somewhere as it' s a bit difficult to read in the video? Thanks!

    Cheers

    Bärbel

    • Hi Bärbel Winkler,

       

      I convert the first video to lower resolution, the code is not really clear. 

       

      Code of the first video:

       

      The interface:

      INTERFACE zif_ca_abap_unit_test1
      
        PUBLIC .
      
        METHODS is_even_whole_number
          IMPORTING
            number        TYPE i
          RETURNING
            VALUE(result) TYPE abap_bool.
      
      ENDINTERFACE.

       

      The Class:

      CLASS zcl_ca_abap_unit_test1 DEFINITION
        PUBLIC
        FINAL
        CREATE PUBLIC .
      
        PUBLIC SECTION.
          INTERFACES zif_ca_abap_unit_test1.
      
        PRIVATE SECTION.
          METHODS get_unit_of_integer_number
            IMPORTING
              number        TYPE i
            RETURNING
              VALUE(result) TYPE char1.
      
      ENDCLASS.
      
      
      
      CLASS zcl_ca_abap_unit_test1 IMPLEMENTATION.
      
        METHOD zif_ca_abap_unit_test1~is_even_whole_number.
          result = switch #( get_unit_of_integer_number( number )
                               when 0 or 2 or 4 or 6 or 8
                                 then abap_true
                                 else abap_false ).
        ENDMETHOD.
      
      
        METHOD get_unit_of_integer_number.
          data(string_number) = conv string( number ).
          DATA(number_size)   = strlen( string_number ).
          DATA(unit_position) = number_size - 2.
          result = string_number+unit_position(1).
        ENDMETHOD.
      
      
      ENDCLASS.

       

       

      The Local Test Class:

      CLASS ltc_ca_abap_unit_test1 DEFINITION
            FOR TESTING
            RISK LEVEL HARMLESS
            DURATION SHORT
            FINAL.
      
        PRIVATE SECTION.
      
          DATA o_cut TYPE REF TO zif_ca_abap_unit_test1.
      
          METHODS it_finds_even_number_1    FOR TESTING.
      
      ENDCLASS.
      
      
      CLASS ltc_ca_abap_unit_test1 IMPLEMENTATION.
      
      
        METHOD it_finds_even_number_1.
          
          " Given I have a number
          data(my_number) type i value 132.
      
          " When I call my method.
          o_cut = NEW zcl_ca_abap_unit_test1( ).
          data(result_of_test) = o_cut->is_even_whole_number( my_number ).
      
          " Then, it should give me positive result
          cl_abap_unit_assert=>assert_equals(
              act = result_of_test
              exp = abap_true ).
      
        ENDMETHOD.
      
      ENDCLASS.
      

       

       

       

       

      And the Local test Class for the second video:

      CLASS ltc_ca_abap_unit_test1 DEFINITION
            FOR TESTING
            RISK LEVEL HARMLESS
            DURATION SHORT
            FINAL.
      
        PRIVATE SECTION.
          TYPES : BEGIN OF ts_even_test,
                    number TYPE i,
                    result TYPE abap_bool,
                  END   OF ts_even_test,
                  tt_even_tests TYPE STANDARD TABLE OF ts_even_test WITH NON-UNIQUE DEFAULT KEY.
      
      
          DATA o_cut TYPE REF TO zif_ca_abap_unit_test1.
      
          METHODS setup.
      
          METHODS it_finds_even_number_1    FOR TESTING.
      
          METHODS given_i_have_even_examples
            RETURNING
              VALUE(examples) TYPE tt_even_tests.
      
      
      
      ENDCLASS.
      
      
      CLASS ltc_ca_abap_unit_test1 IMPLEMENTATION.
      
      
        METHOD setup.
          o_cut = NEW zcl_ca_abap_unit_test1( ).
          cl_abap_unit_assert=>assert_bound( o_cut ).
        ENDMETHOD.
      
      
      
        METHOD it_finds_even_number_1.
      
          LOOP AT given_i_have_even_examples( )
               REFERENCE INTO DATA(o_example).
            " Then, it should give me positive result
            cl_abap_unit_assert=>assert_equals(
              act = o_cut->is_even_whole_number( o_example->number )
              exp = o_example->result
              ).
          ENDLOOP.
      
        ENDMETHOD.
      
      
      
        METHOD given_i_have_even_examples.
          examples = VALUE #( (  number = 1   result = abap_false )
                              (  number = 2   result = abap_true  )
                              (  number = 11  result = abap_false )
                              (  number = 12  result = abap_true  ) ).
        ENDMETHOD.
      
      
      
      ENDCLASS.

       

      Thanks for your comment !

      Best regards

      Fred

    • The code of the third example:

       

      The Data Interface:

      INTERFACE zif_ca_abap_unit_test2a
        PUBLIC .
        METHODS get_material_type
          IMPORTING
            material_number      TYPE char40
          RETURNING
            VALUE(material_type) TYPE char4
          RAISING
            zcx_ca_abap_unit_test2.
      
      ENDINTERFACE.

       

      The Data Class:

      CLASS zcl_ca_abap_unit_test2a DEFINITION
        PUBLIC
        FINAL
        CREATE PUBLIC .
      
        PUBLIC SECTION.
          INTERFACES zif_ca_abap_unit_test2a.
      
      ENDCLASS.
      
      
      
      CLASS zcl_ca_abap_unit_test2a IMPLEMENTATION.
      
        METHOD zif_ca_abap_unit_test2a~get_material_type.
          " Select blabla
      
          " If nothing found -> bigboumbadaboum
          RAISE EXCEPTION TYPE zcx_ca_abap_unit_test2
            EXPORTING
              textid = zcx_ca_abap_unit_test2=>material_does_not_exist.
        ENDMETHOD.
      
      ENDCLASS.
      

       

      The Mock Data Class:

      CLASS zcl_ca_abap_unit_test2a_mock DEFINITION
        PUBLIC
        FINAL
        CREATE PUBLIC .
      
        PUBLIC SECTION.
          TYPES: BEGIN OF ts_material_type,
                   material_number TYPE char40,
                   material_type   TYPE char4,
                 END   OF ts_material_type,
                 tt_material_types TYPE STANDARD TABLE OF ts_material_type WITH NON-UNIQUE DEFAULT KEY.
      
          INTERFACES zif_ca_abap_unit_test2a.
      
          METHODS set_material_type
            IMPORTING
              material_types TYPE tt_material_types.
      
      
        PRIVATE SECTION.
          DATA gt_material_types TYPE tt_material_types.
      
      ENDCLASS.
      
      
      
      CLASS zcl_ca_abap_unit_test2a_mock IMPLEMENTATION.
      
        METHOD set_material_type.
          gt_material_types = material_types.
        ENDMETHOD.
      
        METHOD zif_ca_abap_unit_test2a~get_material_type.
          READ TABLE gt_material_types
               REFERENCE INTO DATA(o_material_type)
               WITH KEY material_number = material_number.
          IF sy-subrc NE 0.
            RAISE EXCEPTION TYPE zcx_ca_abap_unit_test2
              EXPORTING
                textid = zcx_ca_abap_unit_test2=>material_does_not_exist.
          ELSE.
            material_type = o_material_type->material_type.
          ENDIF.
        ENDMETHOD.
      
      ENDCLASS.

       

       

      The Exception Class:

      CLASS zcx_ca_abap_unit_test2 DEFINITION
        PUBLIC
        INHERITING FROM cx_static_check
        FINAL
        CREATE PUBLIC .
      
        PUBLIC SECTION.
      
          INTERFACES if_t100_dyn_msg .
          INTERFACES if_t100_message .
      
          constants:
             begin of material_does_not_exist,
               msgid type symsgid value 'Z_ABAP_UNIT_TEST',
               msgno type symsgno value '001',
               attr1 type scx_attrname value '',
               attr2 type scx_attrname value '',
               attr3 type scx_attrname value '',
               attr4 type scx_attrname value '',
             end   of material_does_not_exist.
      
          METHODS constructor
            IMPORTING
              !textid   LIKE if_t100_message=>t100key OPTIONAL
              !previous LIKE previous OPTIONAL .
      
        PROTECTED SECTION.
        PRIVATE SECTION.
      ENDCLASS.
      
      
      
      CLASS zcx_ca_abap_unit_test2 IMPLEMENTATION.
      
      
        METHOD constructor ##ADT_SUPPRESS_GENERATION.
          CALL METHOD super->constructor
            EXPORTING
              previous = previous.
          CLEAR me->textid.
          IF textid IS INITIAL.
            if_t100_message~t100key = if_t100_message=>default_textid.
          ELSE.
            if_t100_message~t100key = textid.
          ENDIF.
        ENDMETHOD.
      ENDCLASS.

       

       

      The Rules Interface:

      INTERFACE zif_ca_abap_unit_test2b
      
        PUBLIC .
        METHODS is_material_for_sales_type
          IMPORTING
            material_number TYPE char40
            sales_type      TYPE char4
          RETURNING
            VALUE(result)   TYPE abap_bool
          RAISING
            zcx_ca_abap_unit_test2.
      
      
      ENDINTERFACE.
      

       

       

      The Rules Class:

      CLASS zcl_ca_abap_unit_test2b DEFINITION
        PUBLIC
        FINAL
        CREATE PUBLIC .
      
        PUBLIC SECTION.
          INTERFACES zif_ca_abap_unit_test2b.
      
          METHODS constructor
            IMPORTING
              io_material_data TYPE REF TO zif_ca_abap_unit_test2a OPTIONAL.
      
          DATA o_material_data TYPE REF TO zif_ca_abap_unit_test2a.
      
      
      ENDCLASS.
      
      
      CLASS zcl_ca_abap_unit_test2b IMPLEMENTATION.
      
      
        METHOD constructor.
          o_material_data = cond #( when io_material_data is bound
                                      then io_material_data
                                      else NEW zcl_ca_abap_unit_test2a( ) ).
        ENDMETHOD.
      
      
        METHOD zif_ca_abap_unit_test2b~is_material_for_sales_type.
      
          TRY.
              DATA(material_type) = o_material_data->get_material_type( material_number ).
            CATCH zcx_ca_abap_unit_test2.
              RAISE EXCEPTION TYPE zcx_ca_abap_unit_test2
                EXPORTING
                  textid = zcx_ca_abap_unit_test2=>material_does_not_exist.
          ENDTRY.
      
          result = COND #( WHEN material_type EQ 'M001' AND sales_type = 'ST01'
                             THEN abap_true
                           WHEN material_type EQ 'M001' AND sales_type = 'ST02'
                             THEN abap_true
                           ELSE abap_false ).
      
        ENDMETHOD.
      
      ENDCLASS.

       

       

      The Local Test Class:

      CLASS ltc_ca_abap_unit_test2b DEFINITION
            FOR TESTING
            RISK LEVEL HARMLESS
            DURATION SHORT
            FINAL.
      
        PRIVATE SECTION.
          DATA o_cut           TYPE REF TO zif_ca_abap_unit_test2b.
          DATA o_material_data TYPE REF TO zcl_ca_abap_unit_test2a_mock.
      
          METHODS setup.
      
          METHODS it_finds_correct_material_type FOR TESTING.
          METHODS it_failes_material_number      FOR TESTING.
      
      
      ENDCLASS.
      
      
      CLASS ltc_ca_abap_unit_test2b IMPLEMENTATION.
      
        METHOD setup.
          o_material_data = NEW #( ).
          cl_abap_unit_assert=>assert_bound( o_material_data ).
      
          o_cut = NEW zcl_ca_abap_unit_test2b(  o_material_data ).
          cl_abap_unit_assert=>assert_bound( o_cut ).
        ENDMETHOD.
      
      
        METHOD it_finds_correct_material_type.
      
          " Given I have material number / material type
          o_material_data->set_material_type( VALUE #(
                             ( material_number = '0001' material_type = 'M001' )
                             ( material_number = '0002' material_type = 'M002' ) ) ).
          " When I call for material 0001 & Sales type T001 it should be true.
          cl_abap_unit_assert=>assert_equals(
            act = o_cut->is_material_for_sales_type( material_number = '0001'
                                                     sales_type      = 'ST01' )
            exp = abap_true  ).
      
          " When I call for material 0002 & Sales type T001 it should be true.
          cl_abap_unit_assert=>assert_equals(
            act = o_cut->is_material_for_sales_type( material_number = '0002'
                                                     sales_type      = 'ST01' )
            exp = abap_false  ).
      
        ENDMETHOD.
      
      
        METHOD it_failes_material_number.
          " Given I have material number / material type
          o_material_data->set_material_type( VALUE #(
                             ( material_number = '0003' material_type = 'M001' )
                             ( material_number = '0002' material_type = 'M002' ) ) ).
          TRY.
              " When I call for material 0001 & Sales type T001 it should be true.
              cl_abap_unit_assert=>assert_equals(
                act = o_cut->is_material_for_sales_type( material_number = '0001'
                                                         sales_type      = 'ST01' )
                exp = abap_true  ).
            CATCH zcx_ca_abap_unit_test2 INTO DATA(o_exception).
          ENDTRY.
          cl_abap_unit_assert=>assert_bound( o_exception ).
        ENDMETHOD.
      
      
      
      ENDCLASS.

       

      • Third example

         

         

        The interface:

        interface ZIF_CA_ABAP_UNIT_TEST3
          public .
            methods is_integer_prime_number
              importing
                number_to_test type i
              returning
                value(result) type abap_bool.
        
        endinterface.

         

         

        The Class:

        
        CLASS zcl_ca_abap_unit_test3 DEFINITION
          PUBLIC
          FINAL
          CREATE PUBLIC .
        
          PUBLIC SECTION.
            INTERFACES zif_ca_abap_unit_test3.
        
          PRIVATE SECTION.
        
            METHODS is_integer_multiple_of
              IMPORTING
                number_to_test TYPE i
                multiple       TYPE i
              RETURNING
                VALUE(result)  TYPE abap_bool.
        
        
        ENDCLASS.
        
        
        
        CLASS zcl_ca_abap_unit_test3 IMPLEMENTATION.
        
        
          METHOD zif_ca_abap_unit_test3~is_integer_prime_number.
        
            DATA remainder TYPE i.
            DATA divider   TYPE i.
        
            " Result is true, until I prove the opposite
            result = abap_true.
        
            DO number_to_test TIMES.
        
              IF sy-index = 1.
                CONTINUE.
              ENDIF.
              divider = sy-index.
              IF divider EQ number_to_test.
                RETURN.
              ENDIF.
        
              IF is_integer_multiple_of( number_to_test = number_to_test
                                         multiple       = divider       ) eq abap_true.
                result = abap_false.
                RETURN.
              ENDIF.
        
            ENDDO.
        
          ENDMETHOD.
        
        
        
        
          method is_integer_multiple_of.
            data remainder type i.
            remainder = number_to_test MOD multiple.
            result = cond #( when remainder eq 0
                               then abap_true
                               else abap_false ).
          endmethod.
        
        
        ENDCLASS.

         

         

        The test class:

        CLASS ltc_test_prime DEFINITION
              FOR TESTING
              RISK LEVEL HARMLESS
              DURATION SHORT
              FINAL.
        
          PRIVATE SECTION.
        
            TYPES: BEGIN OF ts_number_to_test,
                     value TYPE i,
                     prime TYPE abap_bool,
                   END OF ts_number_to_test,
                   tt_number_to_test TYPE STANDARD TABLE OF ts_number_to_test
                                     WITH NON-UNIQUE DEFAULT KEY.
        
            METHODS setup.
        
            METHODS class_could_be_bounded       FOR TESTING.
            METHODS it_finds_number_is_prime     FOR TESTING.
        
            METHODS given_i_have_numbers
              RETURNING
                VALUE(results) TYPE tt_number_to_test.
        
            DATA o_cut TYPE REF TO zif_ca_abap_unit_test3.
        
        
        ENDCLASS.
        
        
        
        CLASS ltc_test_prime IMPLEMENTATION.
        
        
          METHOD setup.
            o_cut = NEW zcl_ca_abap_unit_test3(  ).
          ENDMETHOD.
        
        
          METHOD class_could_be_bounded.
            cl_abap_unit_assert=>assert_bound( o_cut ).
          ENDMETHOD.
        
        
          METHOD it_finds_number_is_prime.
            LOOP AT given_i_have_numbers( )
                REFERENCE INTO DATA(number_to_test).
              cl_abap_unit_assert=>assert_equals(
                act = o_cut->is_integer_prime_number( number_to_test->value )
                exp = number_to_test->prime ).
            ENDLOOP.
          ENDMETHOD.
        
        
          METHOD given_i_have_numbers.
            results = VALUE #(  ( value = '3'   prime = abap_true )
                                ( value = '5'   prime = abap_true )
                                ( value = '127' prime = abap_true )
                                ( value = '900' prime = abap_false )
                                ( value = '963' prime = abap_false ) ).
          ENDMETHOD.
        
        
        ENDCLASS.

         

  • Hi Fred,

    Thanks for your videos, they are really useful.

    I have a question regarding eclipse:

    How is it possible to to display the class under test and the test class in the same time with different windows?

    I've seen it in your videos, I prefer to work that way, but never found the way to archive it (apart from two SAPGUI sessions 🙂 )

    Thanks,

    Peter

  • Hi Frederic Girod

    Congratulations for the initiative. It's very well done and enlightening.

    For the code sharing I would suggest you use abapGit tool.

    And I have a question for you regarding your eclipse theme setup.

    I am using the dark theme like you but I have some dark over dark issues that I see you don't have:

    Did you change any of your settings?

    Thanks again and waiting for the next videos. Maybe Unit Testing an OData service?

    A+

    Sérgio

    /
    • Hi Sergio,

       

      thanks for your feedback.

      I have two computers, one is like your, the second is like my screenshot.

      The configuration is the same on both, the eclipse installation is older in the computer with the issu.

      Menu: Window > Preferences > DevStyle > Color Themes

       

       

      Best regards

      Fred

  • Come on, your English is not that terrible 😉

    One suggestion with respect to source code: you could publish it on a GH repo with abapGit and just put the link in the video description. More compact and allows for instant updates if you later spot an error.

    Thanks for the videos, much appreciated.

    P.S.: I think I'll do the repo and then share it back, as thanks 🙂

    Here is the repo!

    • Hi Andrea,

       

      thanks for the idea!

      I am totaly lost in GitHub, I thought the Repo was empt y and I push a file, but ... and now I don't know how to suppress my file. I will take time to read GitHub Documentation

       

       

      Fred

      • For a quick start, you could clone my repo (link above) and it's all there.

        If you need to familiarize yourself with git and GH first, there is plenty of documentation around, you'll have no problems there.

  • Ops, forgot the code for the last video, just added the commit.

    Quick question: why the boolean comparison in calling is_integer_multiple_of?

    It's a boolean return value, it works as is.