This is a test message to test the length of the message box.
Login
ABAP Unit test framework
Created by Software-Heroes

ABAP Unit - Test framework

1281

This article is about testing private methods and simply disabling dependent on components in code.



With the test framework, SAP offers additional tools and options for setting up clean tests. These include test doubles, SEAMs and the testing of private methods; we will take a closer look at the last two techniques in this article.

 

Test object

In order to better understand the object to be tested, we have listed the source code again here. The class consists of a public method and a private method. The private method gets data from two tables and returns it. The public method determines the data and outputs it via an additional component. If the output worked, a corresponding return flag is set.

CLASS zcl_bs_demo_private_access DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    METHODS:
      display_tools
        RETURNING VALUE(rd_displayed) TYPE abap_bool.

  PROTECTED SECTION.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF ts_tools,
        short_name     TYPE zbs_dy_tools-short_name,
        stock_quantity TYPE zbs_dy_tools-stock_quantity,
        description    TYPE zbs_dy_toolst-description,
      END OF ts_tools,
      tt_tools TYPE STANDARD TABLE OF ts_tools WITH EMPTY KEY.

    METHODS:
      get_tool_data
        RETURNING VALUE(rt_tools) TYPE tt_tools.
ENDCLASS.



CLASS zcl_bs_demo_private_access IMPLEMENTATION.
  METHOD display_tools.
    DATA(lt_tools) = get_tool_data( ).

    TRY.
        rd_displayed = NEW zcl_test_display( )->display_generic_data( lt_tools ).
      CATCH cx_sy_data_access_error.
        rd_displayed = abap_false.
    ENDTRY.
  ENDMETHOD.


  METHOD get_tool_data.
    TRY.
        DATA(ld_language) = cl_abap_context_info=>get_user_language_abap_format( ).
      CATCH cx_abap_context_info_error.
        RETURN.
    ENDTRY.

    SELECT tool~short_name, tool~stock_quantity, text~description
      FROM zbs_dy_tools AS tool
      LEFT OUTER JOIN zbs_dy_toolst AS text
        ON text~short_name = tool~short_name
        AND text~language = @ld_language
      INTO TABLE @rt_tools.
  ENDMETHOD.
ENDCLASS.

 

Private methods

In different situations it can be important to test the private methods and attributes of a class. Here you should be aware that the most stable interface are the public methods of the class and that the internal part of the class can quickly look different with a simple refactoring.

So why do you test the private part of classes?

  • “Interface class” - Usually has a processing method that starts all processing and has a large number of private methods with corresponding logic
  • Using Injection - The private part of the class must be accessed for backdoor injection, more on this in the following chapters
  • Important Methods - There are important and critical methods within the class that should be tested

 

Now let's look at the example above and consider the data discovery method to be particularly important and want to test it in a separate test case. In order to be able to access the private attributes and methods, we have to “befriend” the test class with the global class. Global friends have been around for a long time in the OO context in ABAP, local friends are still relatively new and are mainly used for test classes.

" 1 - Friends on TOP
CLASS ltc_private_method DEFINITION DEFERRED.
CLASS zcl_bs_demo_private_access DEFINITION LOCAL FRIENDS ltc_private_method.

CLASS ltc_private_method DEFINITION FINAL FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    METHODS:
      get_full_database FOR TESTING RAISING cx_static_check.
ENDCLASS.

" 2 - Friends after definition
CLASS zcl_bs_demo_private_access DEFINITION LOCAL FRIENDS ltc_private_method.

 

You have the option to define the friendship at the top of the test include before you define the test class (1) or to befriend it after the definition (2). Both options have advantages and disadvantages, so you can easily overlook the definition below or, as in the first case, you have to create the definition of the test class twice and announce it beforehand.

CLASS ltc_private_method IMPLEMENTATION.
  METHOD get_full_database.
    DATA(lo_cut) = NEW zcl_bs_demo_private_access( ).

    DATA(lt_result) = lo_cut->get_tool_data( ).

    cl_abap_unit_assert=>assert_not_initial( lt_result ).
  ENDMETHOD.
ENDCLASS.

 

When implementing the test case, we can now access the private methods and attributes and thus test the selection individually without having to execute the output.

 

SEAM

In some cases we may be working with completely old coding and would have to change a lot for proper testability or it cannot be produced at all. In such cases we have to completely deactivate disruptive calls and components. The easiest way to do this is with the test seam. These are two small statements with which we can achieve our goal. For the test we adapt the method “display_tools” and place the method call in the test team “display”.

METHOD display_tools.
  DATA(lt_tools) = get_tool_data( ).

  TRY.
      TEST-SEAM display.
        rd_displayed = NEW zcl_bs_demo_display( )->display_generic_data( lt_tools ).
      END-TEST-SEAM.

    CATCH cx_sy_data_access_error.
      rd_displayed = abap_false.
  ENDTRY.
ENDMETHOD.

 

The coding is now prepared accordingly for our test cases. Now it's about the implementation of the test cases and what actually does such a test seam. We have prepared the entire test class for you.

CLASS ltc_seam DEFINITION FINAL FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PRIVATE SECTION.
    METHODS:
      no_seam FOR TESTING,
      empty_seam FOR TESTING,
      returning_seam FOR TESTING.
ENDCLASS.


CLASS ltc_seam IMPLEMENTATION.
  METHOD empty_seam.
    DATA(lo_cut) = NEW zcl_bs_demo_private_access( ).

    TEST-INJECTION display.
    END-TEST-INJECTION.

    DATA(ld_result) = lo_cut->display_tools( ).

    cl_abap_unit_assert=>assert_false( ld_result ).
  ENDMETHOD.


  METHOD no_seam.
    DATA(lo_cut) = NEW zcl_bs_demo_private_access( ).

    DATA(ld_result) = lo_cut->display_tools( ).

    cl_abap_unit_assert=>assert_false( ld_result ).
  ENDMETHOD.


  METHOD returning_seam.
    DATA(lo_cut) = NEW zcl_bs_demo_private_access( ).

    TEST-INJECTION display.
      rd_displayed = abap_true.
    END-TEST-INJECTION.

    DATA(ld_result) = lo_cut->display_tools( ).

    cl_abap_unit_assert=>assert_true( ld_result ).
  ENDMETHOD.
ENDCLASS.

 

How exactly do the individual methods behave? At this point we want to break down the behavior:

  • Method no_seam - No test injection is called, so the code in the method is called normally as if the test seam did not exist.
  • Method emtpy_seam - The test injection in the method is empty, so the test seam in the method to be tested is also not replaced by anything. This means that the dependent class is not called. However, you should make sure that the return value is not set either.
  • Method returning_seam - In the test injection, the return value for the method is set and the dependent class is no longer called. We have now completely deactivated the dependent component and the test always returns an ABAP_TRUE.

 

With test seams and test injections, it is easy to deactivate dependent components in the existing source code and replace them with “lighter” code. For testable code, it is not necessarily the optimal tool of choice because the code behaves differently from situation to situation. It should therefore be testable code from the outset or, in exceptional cases, apply to existing coding in order to make it testable.

 

Conclusion

With our tips, you now have additional methods and resources available to you on how you can set up your tests in order to test code more efficiently and easily.


Included topics:
ABAP UnitABAPUnit TestsTest framework
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP Unit - Test execution

Category - ABAP

What options are there for executing ABAP Unit and what hidden functions might you not know about? More details about the different modes in this article.

06/25/2024

ABAP Unit - Automation

Category - ABAP

When do objects break during development and what side effects can adjustments have? In this article we take a closer look at the topic.

01/05/2024

ABAP Unit - TDF (Function Double)

Category - ABAP

In this article, let's take a look at how we can deal with function modules as dependencies without testing them as well.

04/07/2023

RAP - ABAP Unit (Service)

Category - ABAP

How do you actually test the service of an interface or RAP app automatically? In this article we look at unit tests for OData services.

01/20/2023

RAP - ABAP Unit (Business Object)

Category - ABAP

At this point, we'll take a look at how we can test our RAP Business Object automatically in order to check all important functions at the touch of a button.

01/13/2023