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.