[Unit Tests] ASSERT_THAT – Der unterschätzte Helfer
Unit Tests sind immer noch eine Besonderheit in den meisten ABAP-Programmierungen. Ein Grund aus meiner Sicht ist die Verwendung von hochintegrierten SAP-Standardfunktionalitäten (BAPIs und andere Funktionsbausteine, GUI-Komponenten usw). Deren Verwendung so zu kapseln, dass nur die Geschäftslogik getestet werden kann, ist aufwändig.
Ein weiterer Grund könnte sein, Testfällte schnell kompliziert werden. Notwendige Tests gehen schnell über die Kapazitäten von ASSERT_BOUND oder ASSERT_EQUALS hinaus.
An dieser Stelle kann ASSERT_THAT – die Prüfung mit individuellen Bedingungen (Constraints) – helfen und die Tests übersichtlicher gestalten.
ASSERT What?
Die ASSERT-Methoden der Klassen CL_ABAPUNIT_ASSERT dienen dazu, Erwartungen mit den tatsächlich errechneten Daten abzugleichen. Die Methode ASSERT_THAT ist die komplizierteste, denn für die Benutzung ist die Programmierung einer separaten Klasse mit dem Interface IF_CONSTRAINT. Innerhalb dieser Constraint-Klasse sind umfangreiche Prüfungen möglich.
Dadurch, dass die der Aufruf relativ einfach gehalten werden kann, ist der Testfall selbst sehr übersichtlich.
Beispiel
Ein typischer Testfall sieht eventuell folgendermaßen aus:
METHOD assure_item_is_valid_for_case_xyz. DATA(ergebnis) = cut->do_something( my_object). cl_abap_unit_assert=>assert_equals( exp = 3 act = ergebnis-menge ). cl_abap_unit_assert=>assert_differs( exp = 'ST' act = ergebnis-meins ). cl_abap_unit_assert=>assert_true( ergebnis-valid ). ENDMETHOD.
Das konstruierte Beispiel ist noch recht einfach. Wenn nun jedoch noch fünf Felder und eine interne Tabelle als Ergebnis geliefert wird, dann wird es schnell unübersichtlich. Da wäre es doch praktischer, wenn man schreiben könnte:
METHOD assure_item_is_valid_for_case_xyz. DATA(ergebnis) = cut->do_something( my_object). DATA(constraint_case_xyz) = NEW lcl_constraint_xyz( input = ergebnis ) ). cl_abap_unit_assert=>assert_that( exp = constraint_case_xyz act = ergebnis ). ENDMETHOD.
Die zu programmierenden Prüfungen werden dadurch natürlich nicht weniger, aber die Testfälle werden deutlich übersichtlicher.
Rückmeldung der gescheiterten Prüfungen
Bei den üblichen ASSERT-Methoden kann nur ein String zurückgegeben werden (Parameter MSG), der mehr oder weniger detailliert Auskunft über die abgewichene Erwartung gibt. Bei einem Constraint kann eine Tabelle mit Strings übergeben werden, was auch sinnvoll ist, da in einem Constraint viele Dinge geprüft werden können. Je nachdem, wie viel Mühe man sich gibt, kann man eine sehr sprechende Rückmeldung erstellen.
Einschränkungen von ASSERT_TABLE_CONTAINS
Ein großes Problem bei der Prüfung von internen Tabellen ist, dass man bei der Überprüfung einer internen Tabelle diese genau abbilden muss. Möchte man nur einige wenige für den jeweiligen Testfall relevante Felder prüfen, dann muss man die Tabellenzeilen durchgehen (LOOP) und die Felder separat abfragen.
Es kann mehrere Gründe dafür geben, warum man Feldwerte der einzelnen Felder nicht kennen kann oder nicht kennen muss:
- Die Feldwerte sind schlicht nicht reproduzierbar (Timestamps, GUIDs, Texte, …).
- Die Feldwerte sind nicht relevant für den aktuellen Testfall (Du möchtest die Menge prüfen, aber nicht das Lieferdatum).
- Feldwerte sind egal; Wichtig ist nur, dass ein Wert vorhanden ist (Lieferpriorität, Materialnummer, …).
LOOP AT ergebnistabelle INTO DATA(ergebniszeile). cl_abap_unit_assert=>assert_equals( act = ergebniszeile-meins exp = 'KG' ). cl_abap_unit_assert=>assert_number_between( lower = 1 upper = 5 number = ergebniszeile-stufe ). ENDLOOP.
Generelle Hilfsklassen
Das oben genannte Problem hatte ich in einem Projekt und habe eine Lösung dafür entwickelt. Ich habe zwei Klassen geschrieben, die generell eingesetzt werden können:
- ZCL_CAT_CHECK_LINE_EXISTS prüft, ob bestimmte Tabellenzeilen in einer Tabelle vorhanden sind
- ZCL_CAT_MATCH_FIELDS prüft in Strukturen und Tabellen, ob bestimmte Feldwerte gesetzt sind
Zum Einsatz kommt auch die Hilfsklasse ZCL_CAT_FIELD_MATCHER
ZCL_CAT_CHECK_LINE_EXISTS
Die Verwendung ist denkbar einfach: Es muss eine Instanz der Klasse erzeugt werden, bei der die gewünschte Struktur mit den gewünschten Schlüsselfeldern übergeben werden sowie der Information, welches die zu prüfenden Schlüsselfelder sind. Die Klasse ist der Constraint, der ASSERT_THAT zusammen mit der zu prüfenden Tabelle übergeben werden kann:
DATA(act_demo) = VALUE tt_demo(
( intg = 1 char = 'test1' date = '20240601' time = '120000' numc = '12345' )
( intg = 2 char = 'test2' date = '20240602' time = '130000' numc = '54321' ) ).
DATA(exp_data) = VALUE ts_demo( intg = 1 ).
DATA cut TYPE REF TO if_constraint.
cut = NEW zcl_cat_check_line_exists( exp_data = REF #( exp_data )
key_fields = VALUE #( ( |INTG| ) ) ).
cl_abap_unit_assert=>assert_that( exp = cut act = act_demo ).
ZCL_CAT_MATCH_FIELDS
Diese Klasse prüft, ob einzelne Felder einen bestimmten Wert haben. Das ist besonders praktisch, bei breiten Strukturen oder Tabellen, bei denen nicht alle Feldwerte relevant oder bekannt sind. Auch hier ist die Verwendung einfach:
DATA(act_demo) = VALUE tt_demo(
( intg = 1 char = 'test1' date = '20240601' time = '120000' numc = '12345' )
( intg = 2 char = ' ' date = '20240602' time = '130000' numc = '54321' ) ).
DATA(exp_demo) = VALUE tt_demo(
( intg = 1 char = '*' numc = '12345' )
( intg = 2 char = '!' numc = '54321' ) ).
DATA cut TYPE REF TO if_constraint.
cut = NEW zcl_cat_match_fields( REF #( exp_demo ) ).
cl_abap_unit_assert=>assert_that( exp = cut act = act_demo ).
Besonders hilfreich ist aus meiner Sicht die Verwendung der Wildcards “*” und “!”. Mit “*” muss das Feld einen Wert enthalten. Welcher Wert das genau ist, ist egal. Das Pendant dazu ist “!” bei dem sichergestellt sein muss, dass der Wert initial ist (funktioniert nur bei zeichenbasierten Feldern).
Als Ergebnis bei einemm nicht übereinstimmenden Ergebnis erhält man eine aussagekräftige Tabelle:

Weitere Verwendung als ASSERT
Mit dem ABAP-Befehl ASSERT können Bedingungen in produktiven Programmcode eingebaut werden. Dies wird häufig gemacht, um Voraussetzungen für den weiteren Programmablauf sicherzustellen. Wenn ohne diese Bedingung eine sinnvolle Bearbeitung nicht möglich ist, dann bricht das Programm hier mit einem Shortdump ab.
Beispiel:
ASSERT sy-ubrc = 0.
Die erstellten Constraints können ebenfalls für einen solchen Fall genutzt werden. Auf diese Weise kann in produktiven Prozessen die Richtigkeit und Gültigkeit bestimmter Datenzustände geprüft werden:
DATA(my_constraint) = NEW zcl_my_constraint( ). ASSERT my_constraint( )->is_valid( object ).
Natürlich kann die Bedingung auch ohne ASSERT in den normalen Programmablauf eingebunden werden:
IF NOT my_constraint( )->is_valid( object ). MESSAGE 'Bedingung XYZ ist nicht gegeben' TYPE 'E'. ENDIF.
Fazit
Mit Hilfe von Constraints kann man komplizierte Bedingungen programmieren und diese relativ unkompliziert in Unit Tests verwenden. Mit etwas Geschick und Übung lassen sich logische Objektprüfungen programmieren, die auch in produktiven Anwendungen Platz finden können. Die Verwendung erfordert etwas Übung, ist dann jedoch logisch und einfach.
Github-Repository
Den kompletten Quellcode findest du auf Github. Verwende abapGit zum Download der Klassen.
- [Unit Tests] ASSERT_THAT – Der unterschätzte Helfer - 7. September 2026
- FILTER - 13. Januar 2026
- Automatisches Eingabefeld [SAPGUI] - 9. Oktober 2025


