This is a test message to test the length of the message box.
Login
ABAP RAP ABAP Unit
Erstellt von Software-Heroes

RAP - ABAP Unit (Business Objekt)

134

An dieser Stelle schauen wir uns einmal an, wie wir unser RAP Business Objekt automatisch testen können, um so alle wichtigen Funktionen per Knopfdruck zu prüfen.

Werbung


In einer älteren Serie haben wir uns bereits ABAP Unit angeschaut und wie du damit deinen Code automatisch testen lassen kannst. In diesem Artikel schauen wir uns an, wie du dein RAP Objekt validieren lassen kannst und was du am sinnvollsten prüfen kannst.

 

ABAP Unit

Unter dem Begriff fallen alle Methoden zum automatischen Testen von ABAP Code im System, dabei gibt es verschiedene Techniken den Code testbar zu machen. Auf unserer Übersichtsseite findest du alle zu ABAP Unit und wie du es anwenden kannst, in diesem Artikel setzen wir das Wissen als Grundlage voraus.

 

Test schreiben

Für die Testfälle legen wir nun eine globale Testklasse an. Wir könnten zwar eine lokale Testklasse anlegen, wir wollen aber nur die öffentlichen Schnittstellen unseres RAP Objekts testen und den Test vom Originalobjekt trennen. Dazu erstellen wir die folgende Testklasse als Hülle für die Tests:

"! @testing ZBS_I_RAPPartner
CLASS zcl_bs_demo_unit_rap DEFINITION PUBLIC FINAL CREATE PUBLIC
  FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
    METHODS:
      create_new_entry FOR TESTING,
      fill_empty_streets FOR TESTING,
      clear_empty_streets FOR TESTING.
ENDCLASS.


CLASS zcl_bs_demo_unit_rap IMPLEMENTATION.
  METHOD create_new_entry.

  ENDMETHOD.


  METHOD fill_empty_streets.

  ENDMETHOD.


  METHOD clear_empty_streets.

  ENDMETHOD.
ENDCLASS.

 

Die Testklasse legen wir als globale Klasse an, dazu müssen auch die entsprechenden Zusätze (FOR TESTING, DURATION, RISK LEVEL) angegeben werden. Über den ABAP Doc Kommentar verlinken wir die Testklasse mit dem Core Data Service. Dazu wollen wir drei Tests implementieren:

  • Anlage eines neuen Eintrags und Prüfung des erzeugten Schlüssels
  • Befüllung eines Eintrags mit leerer Straße
  • Löschen aller Einträge mit leerer Straße

 

Neuer Eintrag

Der Test befüllt im ersten Schritt eine Tabelle mit neuen Daten und markiert die Fehler als relevant, dann fügen wir die neuen Datensätze ein und persistieren die Daten per Commit auf der Datenbank. Dann prüfen wir die Rückgabe, ob Fehler enthalten sind. Zuletzt lesen wir von der Datenbank, ob der neue Eintrag auch wirklich angelegt wurde.

DATA:
  lt_new_partner TYPE TABLE FOR CREATE ZBS_I_RAPPartner.

lt_new_partner = VALUE #(
  ( partnername = 'Do it Yourself'
    street = 'Waterloo Street 13'
    city = 'London'
    country = 'GB'
    paymentcurrency = 'GBP'
    %control-PartnerName = if_abap_behv=>mk-on
    %control-Street = if_abap_behv=>mk-on
    %control-City = if_abap_behv=>mk-on
    %control-Country = if_abap_behv=>mk-on
    %control-PaymentCurrency = if_abap_behv=>mk-on
  )
).

MODIFY ENTITIES OF ZBS_I_RAPPartner
  ENTITY Partner CREATE FROM lt_new_partner
  MAPPED DATA(ls_mapped).

COMMIT ENTITIES
  RESPONSE OF ZBS_I_RAPPartner
    REPORTED DATA(ls_commit_reported)
    FAILED DATA(ls_commit_failed).

cl_abap_unit_assert=>assert_initial( ls_commit_reported-partner ).
cl_abap_unit_assert=>assert_initial( ls_commit_failed-partner ).

SELECT SINGLE FROM zbs_dmo_partner
  FIELDS partner, name
  WHERE name = 'Do it Yourself'
  INTO @DATA(ls_partner_found).

cl_abap_unit_assert=>assert_subrc( ).

 

Leere Straße befüllen

Im ersten Schritt übergeben wir einen Partner, für den wir die Aktion ausführen möchten, mit einem Commit bestätigen wir die Aktion und lesen dann von der Datenbank den aktuellen Status nach. Die Straße sollte nun auf "EMPTY" stehen und der Eintrag von der Datenbank gefunden werden.

DATA:
  lt_fill_streets TYPE TABLE FOR ACTION IMPORT ZBS_I_RAPPartnerPartner~fillEmptyStreets.

lt_fill_streets = VALUE #(
  ( PartnerNumber = '2000000001' )
).

MODIFY ENTITIES OF ZBS_I_RAPPartner
  ENTITY Partner EXECUTE fillEmptyStreets FROM lt_fill_streets
  MAPPED DATA(ls_mapped)
  FAILED DATA(ls_failed)
  REPORTED DATA(ls_reported).

COMMIT ENTITIES
  RESPONSE OF ZBS_I_RAPPartner
    REPORTED DATA(ls_commit_reported)
    FAILED DATA(ls_commit_failed).

SELECT SINGLE FROM zbs_dmo_partner
  FIELDS partner, Street
  WHERE partner = '2000000001'
  INTO @DATA(ls_partner_found).

cl_abap_unit_assert=>assert_subrc( ).
cl_abap_unit_assert=>assert_equals( act = ls_partner_found-street exp = 'EMPTY' ).

 

Löschen der leeren Einträge

Für die Ausführung der statischen Aktion müssen wir die Tabelle mit einem leeren Eintrag befüllen, damit die Logik überhaupt ausgeführt wird. Dann wird die Aktion ausgeführt und per Commit bestätigt. Zum Abschluss lesen wir noch einmal die Datenbank, ob es noch Datensätze mit EMPTY gibt.

DATA:
  lt_clear_streets TYPE TABLE FOR ACTION IMPORT ZBS_I_RAPPartnerPartner~clearAllEmptyStreets.

INSERT INITIAL LINE INTO TABLE lt_clear_streets.

MODIFY ENTITIES OF ZBS_I_RAPPartner
  ENTITY Partner EXECUTE clearAllEmptyStreets FROM lt_clear_streets
  MAPPED DATA(ls_mapped)
  FAILED DATA(ls_failed)
  REPORTED DATA(ls_reported).

COMMIT ENTITIES
  RESPONSE OF ZBS_I_RAPPartner
    REPORTED DATA(ls_commit_reported)
    FAILED DATA(ls_commit_failed).

SELECT FROM zbs_dmo_partner
  FIELDS partner
  WHERE street = 'EMPTY'
  INTO TABLE @DATA(lt_empty_streets).

cl_abap_unit_assert=>assert_subrc( exp = 4 ).

 

Nach dem Ausführen des Unit Tests erhalten wir nun ein Ergebnis, doch wird dieser Test immer konsistent sein und können wir immer die gleichen Daten für den Test erwarten? Im nächsten Abschnitt werden wir dieses Problem angehen.

 

Daten mocken

Die ersten Testfälle sind nun erstellt, laufen aber gegen die echten Daten in der Datenbank. Wenn sich diese Daten ändern oder Datensätze gelöscht werden, dann funktionieren unsere Testfälle nicht mehr. Diese Abhängigkeiten wollen wir nun entfernen, in dem wir die Daten auf der Datenbank zum Zeitpunkt des Tests durch neue Testdaten tauschen und somit immer die passenden Testdaten für unsere Testfälle zur Verfügung haben. Dazu arbeiten wir mit einem Test-Double für den dahinterliegenden Core Data Service, das heißt wir brauchen einen CDS Double.

Im ersten Schritt erweitern wir die PRIVATE SECTION und legen eine Variable an, um den Double zu halten. Weiterhin implementieren wir die Methoden CLASS_SETUP und CLASS_TEARDOWN, um die Daten für den Test vorzubereiten:

PRIVATE SECTION.
  CLASS-DATA:
    go_environment TYPE REF TO if_cds_test_environment.

  CLASS-METHODS:
    class_setup RAISING cx_static_check,
    class_teardown.

 

Dann erzeugen wir den Double für den Interface View auf Referenz der Tabelle und aktivieren die Umleitung auf den Test-Double im Framework. Die Teardown Methode wird nur noch implementiert, um am Ende den Test-Double wieder abzubauen.

METHOD class_setup.
  DATA:
    lt_partner TYPE STANDARD TABLE OF zbs_dmo_partner WITH EMPTY KEY.

  go_environment = cl_cds_test_environment=>create(
      i_for_entity = 'ZBS_I_RAPPARTNER'
      i_dependency_list = VALUE #( ( name = 'ZBS_DMO_PARTNER' type ='TABLE' ) )
  ).

  lt_partner = VALUE #(
    ( partner = '2000000001' name = 'Las Vegas Corp' country = 'US' payment_currency = 'USD' )
    ( partner = '2000000002' name = 'Gorillas' street = 'Main street 10' country = 'DE' payment_currency = 'EUR' )
    ( partner = '2000000003' name = 'Tomato Inc' street = 'EMPTY' country = 'AU' payment_currency = 'AUD' )
  ).

  go_environment->insert_test_data( lt_partner ).
  go_environment->enable_double_redirection( ).
ENDMETHOD.


METHOD class_teardown.
  go_environment->destroy( ).
ENDMETHOD.

 

Für unseren Test implementieren wir einen anderen "Nummernkreis", um die Daten von den Originaldaten abzugrenzen. Weiterhin fügen wir nur einige Testdaten, um den Test übersichtlich zu halten.

 

Komplettes Beispiel

Hier noch einmal die gesamte Testklasse mit allen Testmethoden, wie immer findest du diese auch im Git-Repository der Serie:

"! @testing ZBS_I_RAPPartner
CLASS zcl_bs_demo_unit_rap DEFINITION PUBLIC FINAL CREATE PUBLIC
  FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
    CLASS-DATA:
      go_environment TYPE REF TO if_cds_test_environment.

    CLASS-METHODS:
      class_setup RAISING cx_static_check,
      class_teardown.

    METHODS:
      create_new_entry FOR TESTING,
      fill_empty_streets FOR TESTING,
      clear_empty_streets FOR TESTING.
ENDCLASS.


CLASS zcl_bs_demo_unit_rap IMPLEMENTATION.
  METHOD class_setup.
    DATA:
      lt_partner TYPE STANDARD TABLE OF zbs_dmo_partner WITH EMPTY KEY.

    go_environment = cl_cds_test_environment=>create(
        i_for_entity = 'ZBS_I_RAPPARTNER'
        i_dependency_list = VALUE #( ( name = 'ZBS_DMO_PARTNER' type ='TABLE' ) )
    ).

    lt_partner = VALUE #(
      ( partner = '2000000001' name = 'Las Vegas Corp' country = 'US' payment_currency = 'USD' )
      ( partner = '2000000002' name = 'Gorillas' street = 'Main street 10' country = 'DE' payment_currency = 'EUR' )
      ( partner = '2000000003' name = 'Tomato Inc' street = 'EMPTY' country = 'AU' payment_currency = 'AUD' )
    ).

    go_environment->insert_test_data( lt_partner ).
    go_environment->enable_double_redirection( ).
  ENDMETHOD.


  METHOD class_teardown.
    go_environment->destroy( ).
  ENDMETHOD.


  METHOD create_new_entry.
    DATA:
      lt_new_partner TYPE TABLE FOR CREATE ZBS_I_RAPPartner.

    lt_new_partner = VALUE #(
      ( partnername = 'Do it Yourself'
        street = 'Waterloo Street 13'
        city = 'London'
        country = 'GB'
        paymentcurrency = 'GBP'
        %control-PartnerName = if_abap_behv=>mk-on
        %control-Street = if_abap_behv=>mk-on
        %control-City = if_abap_behv=>mk-on
        %control-Country = if_abap_behv=>mk-on
        %control-PaymentCurrency = if_abap_behv=>mk-on
      )
    ).

    MODIFY ENTITIES OF ZBS_I_RAPPartner
      ENTITY Partner CREATE FROM lt_new_partner
      MAPPED DATA(ls_mapped).

    COMMIT ENTITIES
      RESPONSE OF ZBS_I_RAPPartner
        REPORTED DATA(ls_commit_reported)
        FAILED DATA(ls_commit_failed).

    cl_abap_unit_assert=>assert_initial( ls_commit_reported-partner ).
    cl_abap_unit_assert=>assert_initial( ls_commit_failed-partner ).

    SELECT SINGLE FROM zbs_dmo_partner
      FIELDS partner, name
      WHERE name = 'Do it Yourself'
      INTO @DATA(ls_partner_found).

    cl_abap_unit_assert=>assert_subrc( ).
  ENDMETHOD.


  METHOD fill_empty_streets.
    DATA:
      lt_fill_streets TYPE TABLE FOR ACTION IMPORT ZBS_I_RAPPartnerPartner~fillEmptyStreets.

    lt_fill_streets = VALUE #(
      ( PartnerNumber = '2000000001' )
    ).

    MODIFY ENTITIES OF ZBS_I_RAPPartner
      ENTITY Partner EXECUTE fillEmptyStreets FROM lt_fill_streets
      MAPPED DATA(ls_mapped)
      FAILED DATA(ls_failed)
      REPORTED DATA(ls_reported).

    COMMIT ENTITIES
      RESPONSE OF ZBS_I_RAPPartner
        REPORTED DATA(ls_commit_reported)
        FAILED DATA(ls_commit_failed).

    SELECT SINGLE FROM zbs_dmo_partner
      FIELDS partner, Street
      WHERE partner = '2000000001'
      INTO @DATA(ls_partner_found).

    cl_abap_unit_assert=>assert_subrc( ).
    cl_abap_unit_assert=>assert_equals( act = ls_partner_found-street exp = 'EMPTY' ).
  ENDMETHOD.


  METHOD clear_empty_streets.
    DATA:
      lt_clear_streets TYPE TABLE FOR ACTION IMPORT ZBS_I_RAPPartnerPartner~clearAllEmptyStreets.

    INSERT INITIAL LINE INTO TABLE lt_clear_streets.

    MODIFY ENTITIES OF ZBS_I_RAPPartner
      ENTITY Partner EXECUTE clearAllEmptyStreets FROM lt_clear_streets
      MAPPED DATA(ls_mapped)
      FAILED DATA(ls_failed)
      REPORTED DATA(ls_reported).

    COMMIT ENTITIES
      RESPONSE OF ZBS_I_RAPPartner
        REPORTED DATA(ls_commit_reported)
        FAILED DATA(ls_commit_failed).

    SELECT FROM zbs_dmo_partner
      FIELDS partner
      WHERE street = 'EMPTY'
      INTO TABLE @DATA(lt_empty_streets).

    cl_abap_unit_assert=>assert_subrc( exp = 4 ).
  ENDMETHOD.
ENDCLASS.

 

Fazit

Die Implementierung eines Unit Test für ein RAP Business Objekt ist nicht schwer, aber erst mit dem Mocken der Daten von der Datenbank, können auch stabile Tests gebaut werden. Diese Tests sorgen am Ende auch für die sinnvolle Nutzung bei Erweiterungen des Objekts.


Enthaltene Themen:
RAPBTPABAP Unit
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


RAP - Übersetzungs-App (Beispiel)

Kategorie - ABAP

Schauen wir uns einmal ein praktisches Beispiel für die Entwicklung einer RAP Anwendung im ABAP Environment an und wie du mit wenig Aufwand eine App erstellst.

02.08.2024

RAP - Custom Entity Wertehilfe (Deep Dive)

Kategorie - ABAP

Mit der Custom Entity hast du in RAP die meisten Freiheiten in der Entwicklung von ABAP Cloud Anwendungen, doch wie sieht es mit potentiellen Fehlern aus?

12.07.2024

RAP - Deep Action in OData v4

Kategorie - ABAP

In diesem Artikel schauen wir uns einmal Aktionen mit tiefen Strukturen an, wie wir sie erzeugen und Daten an einen API Endpunkt übergeben können.

24.05.2024

BTP - Anbindung On-Premise (Consumption Model v2)

Kategorie - ABAP

In diesem Artikel wollen wir noch einmal einen Update zur Anbindung von On-Premise Systemen geben und wie dies mit einem Communication Arrangement durchgeführt wird.

15.12.2023

RAP - App Count anzeigen (Kachel)

Kategorie - ABAP

In diesem Beispiel geht es um die Anzeige eines Zählers auf der Kachel einer Fiori Elements Anwendung und wie sich so etwas umsetzen lässt.

06.10.2023