ABAP Unit

ABAP Unit Framework

(C) Brandeis Consulting.

Das ABAP Unit Test Framework

Mit diesem Framework lassen sich Tests für Globale Klassen schreiben.

Was wir testen, bleibt uns überlassen. Nicht alles, was mit dem Framework gemacht wird, ist ein Unit Test! Wir können auch Integrationstests mit dem Framework schreiben oder ein Programm zum Löschen einer Datenbanktabelle!

Der Testcode steht auf einem separaten Tabreiter. Auf Produktionssystemen wird dieser Code nicht compiliert und ausgeführt.

Andere Objekttypen

Es gibt auch Unit-Tests für Funktionsgruppen und Programme. Da wir diese aber kaum noch neu entwickeln, beschränken wir uns in der Schulung auf die globalen Klassen. Ggf. können andere Objekte per Test Relation verlinkt werden.

(C) Brandeis Consulting.

Testklassen

UnitTests werden in lokalen Klassen einer globalen Klasse implementiert. In der Definition haben sie den Zusatz FOR TESTING . Durch die Tastenkombination

Strg. + Shift + F10

werden alle Tests einer Klasse ausgeführt.

(C) Brandeis Consulting.

Übung - Erster Test

Code, der getestet werden soll

CLASS zcl_bcg_sort DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
    METHODS sort IMPORTING it_list TYPE tst_string_tab
                 EXPORTING et_list TYPE tst_string_tab.
ENDCLASS.



CLASS zcl_bcg_sort IMPLEMENTATION.
  METHOD sort.

  ENDMETHOD.

ENDCLASS.

Testcode

*"* use this source file for your ABAP unit test classes
class ltcl_sort definition final for testing
  duration short
  risk level harmless.

  private section.
    methods:
      first_test for testing raising cx_static_check.
endclass.


class ltcl_sort implementation.

  method first_test.
    new zcl_bcg_sort( )->sort( 
                            exporting it_list = value #( ( `Z` )
                                                         ( `A` ) )
                            importing et_list = data(Result) ).
    cl_abap_unit_assert=>assert_equals( 
                          act = Result
                          exp = value tst_string_tab( ( `A` )
                                                      ( `Z` ) ) ) .
  endmethod.

endclass.

Optional/Demo: Implementiere einen einfachen Sortieralgorithmus, z.B. Bubblesort.

(C) Brandeis Consulting.

Laufzeit und Risikostufe - Empfehlungen

Laufzeit

Wir legen nach Möglichkeit schnelle Tests an, d.h. solche die deutlich weniger als eine Sekunde dauern. Damit ist die Durchführung jederzeit ohne Warten möglich.

Risikostufe

Unsere Tests verändern Persistente Daten nicht. Wenn wir Logik gegen Datenbankzustände testen wollen, verwenden wir das Test Double Framework.

Falls wir von den obigen beiden Punkten abweichen, brauchen wir einen sehr guten Grund.

(C) Brandeis Consulting.

Laufzeit und Risikostufe - Einstellungen

Bei der Definition der Klasse wird angegeben, welche Laufzeit erwartet wird und wie hoch die Risikostufe ist.

Laufzeiten (DURATION):

  • SHORT - wenige Sekunden
  • MEDIUM - weniger als eine Minute
  • LONG - länger als eine Minute

Risikostufe (RISKLEVEL)

  • HARMLESS - Der Systemstatus wird nicht verändert
  • DANGEROUS - Test verändern persistente Daten
  • CRITITCAL - Systemeinstellungen oder Customizing Daten werden verändert
CLASS ltc_calculate_delta DEFINITION FINAL FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.
(C) Brandeis Consulting.

Setup - Fixture

Das zu testende Objekt (CUT) wird vor dem Test erzeugt und mit einem fest definierten Zustand (aka. Fixture) gebracht.

Falls dieser Schritt nicht trivial ist, wird dazu die Methode SETUP() definiert. Diese wird vor der Ausführung jeder Testmethode vom UnitTest Framework aufgerufen.

Analog dazu wird, falls vorhanden, am Ende die TEARDOWN() Methode aufgerufen. Hier können Dinge nach dem Test aufgeräumt werden.

(C) Brandeis Consulting.

Testmethoden

In den Testklassen werden die privaten Testmethoden definiert. Diese haben keine Parameter. Es können aber Ausnahmen definiert werden, um Laufzeitfehler zu vermeiden.

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

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

Testklassen können auch normale Methoden enthalten, die nicht vom UnitTest Framework aufgerufen werden sollen. Damit können wir unsere Tests besser strukturieren und wiederholenden Code vermeiden.

(C) Brandeis Consulting.

Zeitlicher Ablauf bei Unit Tests

  • Schleife über alle Testklassen einer Klasse
    • Aufruf der statischen Methode CLASS_SETUP()
    • Schleife über alle Testmethoden
      • Erzeugung einer Instanz der Testklasse
      • Aufruf der Instanzmethode SETUP()
      • Aufruf der Testmethode
      • Aufruf der Instanzmethode TEARDOWN()
    • Aufruf der statischen Methode CLASS_TEARDOWN()
    • Ausführen von ROLLBACK WORK
(C) Brandeis Consulting.

Implementierung von Testmethoden

Die Klasse CL_ABAP_UNIT_ASSERT stellt Methoden bereit, mit denen wir unsere Testergebnisse prüfen und dem UnitTest Framework mitteilen können. Beispielsweise

  • ASSERT_EQUALS - prüft auf Gleichheit
  • ASSERT_TRUE bzw. ASSERT_FALSE - prüft logische Aussagen
  • FAIL - Test ist Fehlgeschlagen
    ...

(C) Brandeis Consulting.

Methoden der Klasse CL_ABAP_UNIT_ASSERT

Assert 1/2

  • assert_bound
  • assert_char_cp
  • assert_char_np
  • assert_differs
  • assert_equals
  • assert_equals_float
  • assert_false
  • assert_initial
  • assert_not_bound
  • assert_not_initial

Assert & Fail

  • assert_number_between
  • assert_return_code
  • assert_subrc
  • assert_table_contains
  • assert_table_not_contains
  • assert_text_matches
  • assert_text_not_matches
  • assert_that
  • assert_true
  • fail

Assume & Skip

  • assume_false
  • assume_return_code
  • assume_that
  • assume_true
  • skip

Diese Methode prüfen Vorbedingungen und führen den Rest des Tests nur dann aus, wenn diese erfüllt sind.

Grundsätzlich kommt man fast immer mit den markierten Methoden aus. Am besten verwendet man aber immer die spezifischste Methode. Also

  • ASSERT_INITIAL( act = Wert ) ist besser als
  • ASSERT_EQUALS( act = Wert exp = 0 )
(C) Brandeis Consulting.

Parameter der Methoden von CL_ABAP_UNIT_ASSERT

Die meisten Methoden der Klasse haben den gleichen Parameter:

  • act - Ergebniswert, der geprüft wird
  • exp - Erwartungswert
  • ignore_hash_sequence - Reihenfolge in Hashed Tables ignorieren
  • tol - Tolleranz beim Vergleich von FLOAT - Besser ASSERT_EQUALS_FLOAT verwenden!
  • msg - Nachricht, die beim Scheitern ausgegeben wird.
  • level - Fehlerschwere, Konstanten aus IF_ABAP_UNIT_CONSTANT=>severity. Bei LOW gibt es nur eine Warnung.
  • quit - Normalerweise wird bei einem Fehler die Testmethode beendet. Falls IF_ABAP_UNIT_CONSTANT=>quit-no gesetzt wird, geht es weiter.
    METHODS  assert_Equals
        importing   value(act)              type any
                    value(exp)              type any
                    ignore_Hash_Sequence    type abap_Bool default abap_False
                    tol                     type f optional
                    msg                     type csequence optional
                    level                   type int1 default if_Abap_Unit_Constant=>severity-medium
                    quit                    type int1 default if_Abap_Unit_Constant=>quit-test
        returning   value(assertion_Failed) type abap_Bool,
(C) Brandeis Consulting.

Testabdeckung

Die UnitTests können auch mit Messung der Testabdeckung ausgeführt werden. Eine hohe Testabdeckung ist wünschenswert. Lücken in der Testabdeckung sind Hinweise auf fehlende Testszenarien.

(C) Brandeis Consulting.

Test Relations

Sollen andere Objekte getestet werden, dann sollten wir eine Test Relation herstellen. Also eine Verbindung zwischen der Klasse mit dem Test und dem zu testenden Objekt.

*"* use this source file for your ABAP unit test classes
class ltcl_cds definition final for testing
  duration short
  risk level harmless.

  private section.
    methods:
      "! @testing zi_aunit_user
      "! @raising cx_static_check |
      t_test_open_effort for testing raising cx_static_check.
endclass.

Falls in einer Testmethode andere globale Klassen getestet werden, dann sollte auch hier eine Test Relation hergestellt werden. Dann wird der Test sowohl bei der einen als auch bei der anderen Klasse mit Strg + Shift + F10 ausgeführt.

(C) Brandeis Consulting.

Test Seams

Mit sogenannten Test Seams kann ein Teil des produktiven Codes beim Test ausgewechselt werden. Dieses Konzept gibt es nur im ABAP.

Im produktiven Code

TEST-SEAM TestSeamName.
  [Anweisungen]
END-TEST-SEAM.

Im Unit Test

TEST-INJECTION TestSeamName.
  [Anweisungen]
END-TEST-INJECTION.

Weitere Eigenschaften

  • Jeder Test kann den Seam passend ersetzen
  • Es kann auch mehrere Test Seams in einer Methode geben
  • Test Seams können auch leer definiert werden, z.B. für Dependency Injection
(C) Brandeis Consulting.

Bewertung von Test Seams

Nachteile

  • Man kann große Abhängigkeiten von produktivem Code und Unit Test erzeugen

Vorteile

  • Einfach nachträglich reinzuferkeln, egal wie schlecht die Struktur des Codes ist

Empfehlung

  • Besser mit Test Seam testen als gar nicht
  • Test Seams so klein wie möglich halten
  • Der Code in Test Seams sollte möglichst wenig Verschränkung mit dem umliegenden Code haben und eine klar definierte Aufgabe. Der Code des Test Seams könnte in eine private Methode ausgelagert werden, damit die Schnittstelle zum umliegenden Code wohldefiniert ist
  • Abhängigkeiten zu Instanzdaten sollten gering bleiben
  • Der Name des Test Seams soll klar machen, was die Aufgabe des Seams ist, z.B.

Im produktiven Code

TEST-SEAM DaoDependencyInjection.
  
END-TEST-SEAM.

Im Unit Test

TEST-INJECTION DaoDependencyInjection.
  mo_dao = MyDaoDouble. 
END-TEST-INJECTION.
(C) Brandeis Consulting.