Show TOC

Constructor Dependency Injection for ABAP Unit TestingLocate this document in the navigation structure

Procedure

At design time or when you refactor code, you can enable better unit testing by allowing dependencies to be injected into your code. Your ABAP Unit tests can then substitute test doubles for the production entities wherever you have enabled dependency injection.

You can use any of the established patterns for injecting dependencies in ABAP, like constructor injection, setter injection, or parameter injection. This section shows how you might set up constructor injection as an example for injection patterns in which dependencies are exposed to users of a class.

Constructor Dependency Injection

Instead of creating a needed ABAP object in the code under test, you pass the object to the CUT as an argument of the CONSTRUCTOR method of the class.

In your ABAP Unit test methods, you can then substitute test doubles when you create the test object.

Here is some sample code showing how you might set up constructor injection:

*----------------------------------*
* In CLASS_UNDER_TEST...
*----------------------------------*

*----------------------------------*
* The CONSTRUCTOR sets the private class
* variable MY_DEPENDENCY to reference
* the object of INJECTED_DEPENDENCY.
*----------------------------------*
CLASS class_under_test DEFINITION CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS constructor
      IMPORTING my_injected_dependency TYPE REF TO if_my_dependency.
    METHODS method_under_test
      RETURNING
        value(useful_value) TYPE i .
  PRIVATE SECTION.
    DATA my_dependency TYPE REF TO if_my_dependency.
ENDCLASS.

CLASS class_under_test IMPLEMENTATION.
  METHOD constructor.
    my_dependency = my_injected_dependency.
  ENDMETHOD.
...
ENDCLASS.

*----------------------------------*
* And later, the dependency in use 
* in the code under test
*----------------------------------*
  useful_value = my_dependency->calc_value( ).



*----------------------------------*
* In the local ABAP Unit include
* Goto -> Local Definitions/
*   Implementations -> Local Test 
*   Classes... 
*----------------------------------*

*----------------------------------*
* There is a local test double class
* that reimplements IF_MY_DEPENDENCY
* for use in testing.
*----------------------------------*
  CLASS ltd_test_double DEFINITION.
    PUBLIC SECTION.
      INTERFACES if_my_dependency.
  ENDCLASS.
  CLASS ltd_test_double IMPLEMENTATION.
    METHOD if_my_dependency~calc_value.
      r_value = 1.
    ENDMETHOD.
  ENDCLASS.

*----------------------------------*
* In the ABAP Unit test class, the 
* test method creates a test double
* for MY_INJECTED_DEPENDENCY and
* creates the test object using the 
* double.
*----------------------------------*
CLASS ltc_abap_unit_tests DEFINITION FOR TESTING
  RISK LEVEL HARMLESS DURATION SHORT.
  PRIVATE SECTION.
    METHODS: calc_value_test FOR TESTING ...
ENDCLASS.
      
CLASS ltc_abap_unit_tests IMPLEMENTATION.
  METHOD calc_value_test.
    DATA cut TYPE REF TO class_under_test,
         test_double TYPE REF TO ltd_test_double.
    CREATE OBJECT test_double.
    CREATE OBJECT cut 
      IMPORTING
        my_injected_dependency = test_double.

    useful_value = cut->calc_value( ).

    cl_abap_unit_assert=>assert_equals(
      act   = useful_value
      exp   = 2  ).
  ENDMETHOD.
ENDCLASS.

            

Here is how the sample code works:

  • Instead of creating depended-on objects (dependencies) inline in its code, CLASS_UNDER_TEST in the sample above imports depended-on objects with its CONSTRUCTOR method. This means that CLASS_UNDER_TEST can use separate production and test versions of its dependencies.

  • A local helper class - LTD_TEST_DOUBLE in the sample code - provides a test version of the IF_MY_DEPENDENCY object used by CLASS_UNDER_TEST.

  • In the ABAP Unit test class - LTC_ABAP_UNIT_TESTS in the sample code - the test method creates an instance of the test double. It also creates an instance of the CLASS_UNDER_TEST for use in the test methods in LTC_UNIT_TESTS.

    An alternative to cluttering up the test method with the instantiation of the test double and CUT test object would be to do these tasks in the ABAP Unit SETUP and TEARDOWN methods.

When the ABAP Unit test methods in LTC_UNIT_TESTS run, they use an instance of CLASS_UNDER_TEST that runs with a test double of IF_MY_DEPENDENCY.

In this example, the test double functions as a responder - as stub or fake. It only returns a reliable test value. But you could easily make an observer out of the test double by adding verification methods to it. A test method could then check for example the indirect output, or the data that the code under test provides to the test double.