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 (Service)

182

Wie testet man den Service einer Schnittstelle oder RAP App eigentlich automatisch? In diesem Artikel schauen wir uns Unit Tests für OData Services an.

Werbung


Im letzten Artikel hatten wir uns ABAP Unit für das RAP Business Objekt angeschaut und wie du damit die verschiedenen Funktionen automatisch testen kannst. Heute schauen wir uns die Automatisierung der Tests für den OData Service an und wie du die Umsetzung am besten durchführst.

 

Test schreiben

Im ersten Schritt beginnen wir wieder mit der Hülle der Testklasse und verwenden dabei eine globale Klasse für den Test, die wir über ABAP Doc mit dem Service verbinden, den wir testen. Die Hülle der Klasse sieht wie folgt aus:

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

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF ts_key,
        PartnerNumber TYPE ZBS_C_RAPPartner-PartnerNumber,
      END OF ts_key.

    METHODS:
      get_all_partners FOR TESTING RAISING cx_static_check,
      update_unison FOR TESTING RAISING cx_static_check,
      delete_nevada_inc FOR TESTING RAISING cx_static_check.
ENDCLASS.


CLASS zcl_bs_demo_unit_service IMPLEMENTATION.
  METHOD get_all_partners.
  ENDMETHOD.


  METHOD update_unison.
  ENDMETHOD.


  METHOD delete_nevada_inc.
  ENDMETHOD.
ENDCLASS.

 

Wir definieren drei Tests, um die Funktionen des Service zu validieren:

  • Lesen aller Datensätze
  • Anpassung eines Datensatzes
  • Löschen eines Datensatzes

 

Bevor wir allerdings mit dem Test beginnen, benötigen wir einen Testclient für jeden Test. Hier hast du die Möglichkeit einen für OData V2 oder V4 anzulegen. Da wir unseren V2 Service testen wollen, sieht die Erzeugung wie folgt aus, dabei übergeben wir den Service und die Version:

ro_result = cl_web_odata_client_factory=>create_v2_local_proxy(
    VALUE #( service_id = 'ZBS_UI_SIMPLE_PARTNER_O2' service_version = '0001' )
).

 

Hinweis: Über die Klasse kannst du verschiedene Arten von Clients erzeugen, der Local Proxy wird für die Tests benötigt.

 

Lesen

Nachdem wir uns einen Testclient erzeugt haben, erzeugen wir einen Lese-Request auf der Partner Entität, führen diesen ohne Filter aus und holen uns das Ergebnis zurück. Wir prüfen, ob die Tabelle befüllt ist und ein gewisser Eintrag vorhanden ist:

DATA:
  lt_result TYPE TABLE OF ZBS_C_RAPPartner.

DATA(lo_cut) = create_test_client( ).

DATA(lo_request) = lo_cut->create_resource_for_entity_set( 'Partner' )->create_request_for_read( ).
DATA(lo_response) = lo_request->execute( ).
lo_response->get_business_data( IMPORTING et_business_data = lt_result ).

cl_abap_unit_assert=>assert_not_initial( lt_result ).
cl_abap_unit_assert=>assert_not_initial( lt_result[ PartnerName = 'Unison' ] ).

 

Aktualisieren

Um einen Datensatz zu aktualisieren, erzeugen wir uns einen Update Request über den Client, dazu müssen wir nach der Entität den Schlüssel mitgeben und die Operation PUT wählen. Im Anschluss übergeben wir die neuen Daten und führen die Anfrage aus. Am Ende prüfen wir gegen die Datenbank, ob der Datensatz tatsächlich geändert wurde.

DATA(lo_cut) = create_test_client( ).

DATA(lo_request) = lo_cut->create_resource_for_entity_set( 'Partner'
  )->navigate_with_key( VALUE ts_key( PartnerNumber = '3000000003' )
  )->create_request_for_update( /iwbep/if_cp_request_update=>gcs_update_semantic-put ).

lo_request->set_business_data( VALUE ZBS_C_RAPPartner( Street = 'Updated' ) ).
lo_request->execute( ).

SELECT SINGLE FROM zbs_dmo_partner
  FIELDS *
  WHERE partner = '3000000003'
  INTO @DATA(ls_result).

cl_abap_unit_assert=>assert_equals( act = ls_result-street exp = 'Updated' ).

 

Löschen

Das Löschen funktioniert so ähnlich wie die Aktualisierung, nur das wir am Ende einen Request fürs Löschen erstellen und keine Daten übergeben. Am Ende überprüfen wir, ob der Datensatz von der Datenbank gelöscht wurde.

    DATA(lo_cut) = create_test_client( ).

    DATA(lo_request) = lo_cut->create_resource_for_entity_set( 'Partner'
      )->navigate_with_key( VALUE ts_key( PartnerNumber = '3000000001' )
      )->create_request_for_delete( ).

    lo_request->execute( ).

    SELECT SINGLE FROM zbs_dmo_partner
      FIELDS *
      WHERE partner = '3000000001'
      INTO @DATA(ls_partner).

    cl_abap_unit_assert=>assert_subrc( exp = 4 ).

 

Hinweis: Je nachdem welche Features du implementiert hast, wird Update oder Delete nicht funktionieren im Test. Ohne implementierte Feature Control, funktionieren die Test wie gewohnt.

 

Daten mocken

Bei den Tests verwenden wir aktuell die echten Daten von der Datenbank, was aber nicht für wiederholbare Tests spricht. Deshalb sollten wir die Daten mocken, sodass wir für jeden Test die gleichen Daten verwenden und damit auch sicherstellen, dass die Daten immer zur Verfügung stehen und zwar in dem Stand, den wir benötigen. Dazu definieren wir in der PRIVATE Section noch folgende Variable und Methoden:

CLASS-DATA:
  go_environment TYPE REF TO if_cds_test_environment.

CLASS-METHODS:
  class_setup RAISING cx_static_check,
  class_teardown.

 

Dann können wir CLASS_SETUP implementieren, in diesem Fall erzeugen wir einen CDS Double für den Comsumption View, da der Serivce auf dieser View arbeitet. Als abhängiges Objekt wird auch die Tabelle mit ihren Daten versorgt. Zum Abschluss aktivieren wir noch den Double, damit die Daten korrekt gezogen werden:

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

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

lt_partner = VALUE #(
  ( partner = '3000000001' name = 'Nevada Inc' country = 'US' payment_currency = 'USD' )
  ( partner = '3000000002' name = 'REWE' street = 'Main street 10' country = 'DE' payment_currency = 'EUR' )
  ( partner = '3000000003' name = 'Unison' street = 'Side road 16' country = 'AU' payment_currency = 'AUD' )
).

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

 

Im CLASS_TEARDOWN kümmern wir uns im Anschluss darum, dass der CDS Double auch wieder sauber entfernt wird:

go_environment->destroy( ).

 

Die Daten sind damit nun für jeden Testfall sauber hinterlegt und du brauchst dir keine Sorgen zu machen, wie die Daten auf der Datenbank aussehen. Am Ende erhältst du immer die aktuellen Testdaten für deine Testfälle.

 

Komplettes Beispiel

Das vollständige Beispiel findest du wieder hier am Ende des Artikels und umfasst die komplette Testklasse:

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

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
    TYPES:
      BEGIN OF ts_key,
        PartnerNumber TYPE ZBS_C_RAPPartner-PartnerNumber,
      END OF ts_key.

    CLASS-DATA:
      go_environment TYPE REF TO if_cds_test_environment.

    CLASS-METHODS:
      class_setup RAISING cx_static_check,
      class_teardown.

    METHODS:
      get_all_partners FOR TESTING RAISING cx_static_check,
      update_unison FOR TESTING RAISING cx_static_check,
      delete_nevada_inc FOR TESTING RAISING cx_static_check,

      create_test_client
        RETURNING VALUE(ro_result) TYPE REF TO /iwbep/if_cp_client_proxy
        RAISING
                  cx_static_check.
ENDCLASS.


CLASS zcl_bs_demo_unit_service 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_C_RAPPARTNER'
        i_dependency_list = VALUE #( ( name = 'ZBS_DMO_PARTNER' type ='TABLE' ) )
    ).

    lt_partner = VALUE #(
      ( partner = '3000000001' name = 'Nevada Inc' country = 'US' payment_currency = 'USD' )
      ( partner = '3000000002' name = 'REWE' street = 'Main street 10' country = 'DE' payment_currency = 'EUR' )
      ( partner = '3000000003' name = 'Unison' street = 'Side road 16' 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_test_client.
    ro_result = cl_web_odata_client_factory=>create_v2_local_proxy(
        VALUE #( service_id = 'ZBS_UI_SIMPLE_PARTNER_O2' service_version = '0001' )
    ).
  ENDMETHOD.


  METHOD get_all_partners.
    DATA:
      lt_result TYPE TABLE OF ZBS_C_RAPPartner.

    DATA(lo_cut) = create_test_client( ).

    DATA(lo_request) = lo_cut->create_resource_for_entity_set( 'Partner' )->create_request_for_read( ).
    DATA(lo_response) = lo_request->execute( ).
    lo_response->get_business_data( IMPORTING et_business_data = lt_result ).

    cl_abap_unit_assert=>assert_not_initial( lt_result ).
    cl_abap_unit_assert=>assert_not_initial( lt_result[ PartnerName = 'Unison' ] ).
  ENDMETHOD.


  METHOD update_unison.
    DATA(lo_cut) = create_test_client( ).

    DATA(lo_request) = lo_cut->create_resource_for_entity_set( 'Partner'
      )->navigate_with_key( VALUE ts_key( PartnerNumber = '3000000003' )
      )->create_request_for_update( /iwbep/if_cp_request_update=>gcs_update_semantic-put ).

    lo_request->set_business_data( VALUE ZBS_C_RAPPartner( Street = 'Updated' ) ).
    lo_request->execute( ).

    SELECT SINGLE FROM zbs_dmo_partner
      FIELDS *
      WHERE partner = '3000000003'
      INTO @DATA(ls_result).

    cl_abap_unit_assert=>assert_equals( act = ls_result-street exp = 'Updated' ).
  ENDMETHOD.


  METHOD delete_nevada_inc.
    DATA(lo_cut) = create_test_client( ).

    DATA(lo_request) = lo_cut->create_resource_for_entity_set( 'Partner'
      )->navigate_with_key( VALUE ts_key( PartnerNumber = '3000000001' )
      )->create_request_for_delete( ).

    lo_request->execute( ).

    SELECT SINGLE FROM zbs_dmo_partner
      FIELDS *
      WHERE partner = '3000000001'
      INTO @DATA(ls_partner).

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

 

Fazit

Neben dem Testen des RAP Objekts, kannst du nun auch den Service testen und auch mit Testdaten bestücken, ohne auf den echten Daten zu Arbeiten. Im RAP Umfeld gibt es diese beiden Arten von Tests (Business Objekt und Service).


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