Application Development Blog Posts
Learn and share on deeper, cross technology development topics such as integration and connectivity, automation, cloud extensibility, developing at scale, and security.
cancel
Showing results for 
Search instead for 
Did you mean: 
horst_keller
Product and Topic Expert
Product and Topic Expert

Writing ABAP Unit tests can be somewhat cumbersome if the code to be tested is not suited for automatic tests. All the hubbub about mock frameworks or test driven development isn't worth a cent if you have to deal with code that never came in touch with the concept of separation of concerns. Imagine you have code to maintain, that depends on database contents or calls UI screens and your boss wants you to increase the test coverage of the department - a real life scenario? Yes, at least in my life. If you cannot  redesign and rewrite the whole application, as a workaround you make the code test dependent. This is regarded as bad style, but it helps.

As a simplistic example take a method that gets data from a UI screen but should be tested by a module test. Normally there is no UI available during the test. Setup and teardown methods also do not help as they might do for selecting data from a database by providing test data. A workaround before ABAP 7.50 was a free-style test flag, e.g. as follows:

CLASS cls DEFINITION.
  PUBLIC SECTION.
    METHODS get_input
      RETURNING
        VALUE(input) TYPE string.
  PRIVATE SECTION.
    DATA test_flag TYPE abap_bool.
ENDCLASS.

CLASS cls IMPLEMENTATION.
  METHOD get_input.
    IF test_flag IS INITIAL.
      cl_demo_input=>request( CHANGING field = input ).
    ELSE.
      input = 'xxx'.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

The test method of a test class that is a friend of the class to be tested can influence the method by setting the test flag.

CLASS tst DEFINITION FOR TESTING
          RISK LEVEL HARMLESS
          DURATION SHORT
          FINAL.
  PRIVATE SECTION.
    METHODS test_input FOR TESTING.
ENDCLASS.

CLASS tst IMPLEMENTATION.
  METHOD test_input.
    DATA(oref) = NEW cls( ).
    oref->test_flag = abap_true.
    DATA(input) = oref->get_input( ).
    cl_abap_unit_assert=>assert_equals(
    EXPORTING
      exp = 'xxx'
      act = input ).
  ENDMETHOD.
ENDCLASS.

Bad style and not governed by any conventions. To overcome this, with ABAP 7.50 the concept of test seams and test injections is introduced:

CLASS cls DEFINITION.
  PUBLIC SECTION.
    METHODS get_input
      RETURNING
        VALUE(input) TYPE string.
ENDCLASS.

CLASS cls IMPLEMENTATION.
  METHOD get_input.
    TEST-SEAM fake_input.
      cl_demo_input=>request( CHANGING field = input ).
    END-TEST-SEAM.
  ENDMETHOD.
ENDCLASS.

With TEST-SEAM - END-TEST-SEAM a part of the code is defined as a test seam that can be replaced by test friendly code during testing. No selfdefined attribute is necessary and the test class does not have to be a friend of the class to be tested any more (as long as public methods are tested only). You don't need an alternative implementation inside the production code because this is transferred to the test code  The test method might look as follows now:

CLASS tst DEFINITION FOR TESTING
          RISK LEVEL HARMLESS
          DURATION SHORT
          FINAL.
  PRIVATE SECTION.
    METHODS test_input FOR TESTING.
ENDCLASS.


CLASS tst IMPLEMENTATION.
  METHOD test_input.
    TEST-INJECTION fake_input.
      input = 'xxx'.
    END-TEST-INJECTION.
    DATA(input) = NEW cls( )->get_input( ).
    cl_abap_unit_assert=>assert_equals(
    EXPORTING
      exp = 'xxx'
      act = input ).
  ENDMETHOD.
ENDCLASS.

With TEST-INJECTION - END-TEST-INJECTIONM a test injection is defined that replaces the test seam of the same name during test execution. A test injection can be empty and then simply removes the respective test seam during testing. Test injections can be defined in test includes of global classes and function groups.


For more information, more use cases, and more examples see Test Seams.

30 Comments