Show TOC

Constructor-Dependency-Injection für ABAP-ModultestsLocate this document in the navigation structure

Vorgehensweise

Sie können bessere Modultests erzielen, wenn Sie zur Design-Zeit oder während des Code-Refactoring das Einfügen von Abhängigkeiten in den Code erlauben. Dort wo Sie die Dependency-Injection ermöglicht haben, können Ihre ABAP-Modultests somit Testattrappen (Test-Doubles) gegen die Produktionseinheiten eintauschen.

Sie können für die Dependency-Injection alle in ABAP etablierten Muster, wie die Constructor-Injection, Setter-Injection oder Parameter-Injection, verwenden. Dieser Abschnitt zeigt eine Constructor-Injection beispielhaft für Injection-Muster, in denen Abhängigkeiten den Benutzern einer Klasse exponiert sind.

Constructor-Dependency-Injection

Anstatt ein benötigtes ABAP-Objekt im getesteten Code anzulegen, übergeben Sie das Objekt der getesteten Komponente als Argument der Methode CONSTRUCTOR der Klasse.

Danach können Sie Test-Doubles in Ihre ABAP-Modultestmethoden eintauschen, wenn Sie das Testobjekt anlegen.

Der folgende Abschnitt zeigt ein Code-Beispiel für das Anlegen einer 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.

            

Das Code-Beispiel funktioniert folgendermaßen.

  • Anstatt Dependet-On-Objekte (Abhängigkeiten) inline im Code anzulegen, importiert CLASS_UNDER_TEST im obigen Beispiel Depended-On-Objekte mit seiner CONSTRUCTOR-Methode. Dies bedeutet, dass CLASS_UNDER_TEST separate Produktions- und Testversionen seiner Abhängigkeiten verwenden kann.

  • Eine lokale Hilfsklasse - im obigen Beispiel LTD_TEST_DOUBLE- stellt eine Testversion des IF_MY_DEPENDENCY-Objekts, das von CLASS_UNDER_TEST verwendet wird, zur Verfügung.

  • In der ABAP-Modultestklasse - im obigen Beispiel LTC_ABAP_UNIT_TESTS- legt die Testmethode eine Instanz des Test-Doubles an. Die Testmethode legt weiterhin eine Instanz von CLASS_UNDER_TEST zur Verwendung in den Testmethoden in LTC_UNIT_TESTS an.

    Alternativ können Sie diese Tasks auch in den ABAP-Unit-Methoden SETUP und TEARDOWN ausführen. Hiermit verhindern Sie, dass die Testmethode durch die Instanzierung des Test-Doubles und des Testobjekts der getesteten Komponente zu unübersichtlich wird.

Wenn die ABAP-Modultestmethoden in LTC_UNIT_TESTS ablaufen, verwenden sie eine Instanz der CLASS_UNDER_TEST, die wiederum mit einem Test-Double von IF_MY_DEPENDENCY abläuft.

Im obigen Beispiel fungiert das Test-Double als Responder - als Stub oder Fake. Es liefert nur verlässliche Testwerte zurück. Allerdings können Sie das Test-Double problemlos in einen Observer umwandeln, indem Sie ihm Verifikationsmethoden hinzufügen. Eine Testmethode kann dann beispielsweise den indirekten Output oder die Daten, die der getestete Code dem Test-Double zur Verfügung stellt, prüfen.