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

ABAP Unit - OData testen

In diesem Artikel geht es um die Testbarkeit von OData Services und wie du damit auch Schnittstellen einfach und schnell testen kannst.

Werbung

In diesem Artikel werden wir uns das Testen eines OData Service auf zwei Arten ansehen. Einmal über den klassischen Weg mit einer Data-Provider Klasse und einmal über den neuen Weg eines Service über RAP.

 

Data-Provider Klasse

Allgemein

Um eine Data-Provider Klasse testen zu können, müssen wir zuerst eine Instanz der Klasse, sowie eine Instanz des Kontext-Objekts erzeugen, um überhaupt sauber die Methode testen zu können. Hierfür sind einige Zusatzschritte nötig, damit wir den Provider sauber testen können. Dazu wollen wir dir im ersten Schritt einmal den Testfall etwas näherbringen.

Wir möchten in unserem Test einen OData Service für die folgende Tabelle zur Verfügung stellen, der die typischen CRUD Operationen zur Verfügung stellt. Die Tabelle ist mit allen möglichen Demo-Felder bestückt:

 

Das Legen wir einen Service über die Transaktion SEGW an und importieren die Tabelle als Struktur. Das System wird einen entsprechenden Vorschlag für die Namen der Felder machen. Die Entität benennen wir der Einfachheit “DATA”.

 

Die vollständige Implementierung der DPC-Klasse ZCL_TEST_UNITTEST_DPC_EXT findest du bei den entsprechenden Beispielen am Ende des Buches. Dazu nun die folgende Testklasse, um den Testfall für das Anlegen und Lesen zu implementieren.

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

  PRIVATE SECTION.
    DATA:
      mo_cut                    TYPE REF TO zcl_test_unittest_dpc_ext,
      mo_request_context_object TYPE REF TO /iwbep/cl_mgw_request_unittst.

    METHODS:
      read_data FOR TESTING RAISING cx_static_check,
      create_data FOR TESTING RAISING cx_static_check,

      prepare_cut.
ENDCLASS.


CLASS ltc_odata IMPLEMENTATION.
  METHOD prepare_cut.
    mo_cut = NEW zcl_test_unittest_dpc_ext( ).

    DATA(ls_request_context) = VALUE /iwbep/cl_mgw_request_unittst=>ty_s_mgw_request_context_unit(
      technical_request = VALUE #(
        source_entity_set     = 'DATASet'
        target_entity_set     = 'DATASet'
        source_entity_type    = 'DATA'
        target_entity_type    = 'DATA'
      )
    ).

    mo_request_context_object = mo_cut->/iwbep/if_mgw_conv_srv_runtime~init_dp_for_unit_test( ls_request_context ).
  ENDMETHOD.


  METHOD read_data.
    DATA:
      lt_data TYPE STANDARD TABLE OF zbs_test_data.

    prepare_cut( ).

    DATA(lt_filter) = VALUE /iwbep/t_mgw_select_option(
      ( 
        property = 'CURRENCY' 
        select_options = VALUE #( ( sign = 'I' option = 'EQ' low = 'CHF' ) ) 
      )
    ).

    mo_cut->/iwbep/if_mgw_appl_srv_runtime~get_entityset(
      EXPORTING
        io_tech_request_context  = mo_request_context_object
        it_filter_select_options = lt_filter
      IMPORTING
        er_entityset             = DATA(lr_entity)
        es_response_context      = DATA(ls_response_context)
    ).

    ASSIGN lr_entity->* TO FIELD-SYMBOL(<lt_data>).
    lt_data = CORRESPONDING #( <lt_data> ).

    cl_abap_unit_assert=>assert_equals( act = lines( lt_data ) exp = 2 ).
  ENDMETHOD.


  METHOD create_data.
    DATA:
      ls_new TYPE zbs_test_data.

    prepare_cut( ).

    DATA(ls_new_data) = VALUE zbs_test_data(
      description   = 'A set of testdata from UNIT Test'
      amount        = '12.50'
      currency      = 'USD'
      creation_user = sy-uname
      creation_time = sy-uzeit
      creation_data = sy-datum
    ).
    DATA(lo_data_provider) = NEW /iwbep/cl_cp_v2_entry_provider( REF #( ls_new_data ) ).

    mo_cut->/iwbep/if_mgw_appl_srv_runtime~create_entity(
      EXPORTING
        io_data_provider        = lo_data_provider
        io_tech_request_context = mo_request_context_object
      IMPORTING
        er_entity               = DATA(lr_entity)
    ).

    ASSIGN lr_entity->* TO FIELD-SYMBOL(<ls_data>).
    ls_new = CORRESPONDING #( <ls_data> ).

    cl_abap_unit_assert=>assert_not_initial( ls_new-identifier ).
  ENDMETHOD.
ENDCLASS.

 

Ablauf

Wie sieht es nun mit dem eigentlichen Testfall und der Ausführung aus? Für den Aufbau der Testinstanz müssen einige Schritte vorher ausgeführt werden, da wir auch noch ein Kontext Objekt benötigen, um die Methoden ausführen zu können.

  • Vorbereitung
    • Kontext-Struktur befüllen mit zu testender Entität und dem Namen des Sets
    • Init-Methode der Service-Runtime aufrufen
    • Sichern des Kontext-Objekts in der Testklasse
  • Lesen von Daten
    • Definition eines Filters zur Einschränkung der Datensätze
    • Aufruf der Methode GET_ENTITYSET mit Kontext Objekt und Zusatzdaten (Filter)
    • Prüfung der gefundenen Datensätze für den Test
  • Anlegen von Daten
    • Vorbereitung eines neuen Datensatzes
    • Erstellung eines Datenprovider-Objekts mit den Daten als Referenz
    • Aufruf der Methode CREATE_ENTITY mit Kontext Objekt und Datenprovider
    • Prüfung des neuen Schlüssels für den Test

 

Hinweis: Das Testen von OData Services über diese Methode wird von uns nur teilweise empfohlen, da die Methodik nicht den vollen Funktionsumfang der Services unterstützt. Wenn möglich, sollte auf das Testen mit Service Bindings zurückgegriffen werden.

 

Service Binding

Das Service Bindung ist die neueste Variante einen OData zur Verfügung zu stellen und zwar aus der Implementierung eines Business Objekt über RAP. Dabei stellt Eclipse einen Wizard zur Verfügung der das Coding in einer eigenen Testklasse implementiert oder du schreibst dir deine Tests selbst. Wir möchten dir an dieser Stelle nicht zeigen, wie du per RAP ein eigenes Business Objekt aufbaust, aber wie du aus dem fertigen Service eine Testklasse generierst und diese dann testest. RAP für sich, würde auch wieder ein halbes Buch befüllen.

 

Vorbereitung

Um die Testklasse automatisch über den Wizard anlegen zu können, musst du das Service-Bindung des Datenmodells öffnen, für die du deine Testfälle definieren möchtest. Das Objekt erstellt für einen Service den entsprechenden Endpunkt und das Protokoll (in diesem Fall OData v2) zur Verfügung.

 

Mit einem Rechtsklick auf die entsprechende Entität kannst du dann über das Kontextmenü ein Testklasse generieren.

 

Im Wizard musst du dann nur noch das Paket und den Namen der Testklasse hinterlegen, der Rest wird automatisch vorbelegt. Am Ende wird eine globale Testklasse angelegt, während die eigentlichen Testfälle wieder in den lokalen Testklassen dieser Klasse liegen.

 

Beispiel

Wie schon beim anderen OData-Service implementieren wir beispielhaft für dich den Fall für das Lesen von Datensätzen und die Anlage eines neuen Datensatzes. Die Handhabung des lokalen Proxys ist um einiges einfacher, als das Arbeiten über die DPC Klasse. Für den Proxy gibt es jeweils eine Version für On-Premise und eine für die BTP, je nachdem auf welcher Umgebung du unterwegs bist.

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

  PRIVATE SECTION.
    DATA:
      mo_client_proxy TYPE REF TO /iwbep/if_cp_client_proxy.

    METHODS:
      setup RAISING cx_static_check,

      create_data FOR TESTING RAISING cx_static_check,
      read_data FOR TESTING RAISING cx_static_check.
ENDCLASS.

CLASS ltc_odata_service IMPLEMENTATION.
  METHOD setup.
    " On-Premise Proxy
    mo_client_proxy = /iwbep/cl_cp_client_proxy_fact=>create_v2_local_proxy(
      VALUE #( service_id = 'ZBS_UI_DEMOSIMPLERAP_O2' service_version = '0001' )
    ).

    " BTP Proxy
*    mo_client_proxy = cl_web_odata_client_factory=>create_v2_local_proxy(
*      VALUE #( service_id = 'ZBS_UI_DEMOSIMPLERAP_O2' service_version = '0001' )
*    ).
  ENDMETHOD.


  METHOD create_data.
    DATA:
      ls_business_data TYPE ZBS_C_DemoSimpleRAP,
      lo_request       TYPE REF TO /iwbep/if_cp_request_create,
      lo_response      TYPE REF TO /iwbep/if_cp_response_create.

    ls_business_data = VALUE #(
      Item        = 'HAMMER'
      Description = 'A good tool for nails'
      ItemNumber  = 4911
    ).

    lo_request = mo_client_proxy->create_resource_for_entity_set( 'SimpleRap' )->create_request_for_create( ).
    lo_request->set_business_data( ls_business_data ).
    lo_response = lo_request->execute( ).

    CLEAR ls_business_data.
    lo_response->get_business_data( IMPORTING es_business_data = ls_business_data ).

    cl_abap_unit_assert=>assert_not_initial( ls_business_data-UniqueKey ).
  ENDMETHOD.


  METHOD read_data.
    DATA:
      lt_business_data  TYPE TABLE OF ZBS_C_DemoSimpleRAP,
      lo_request        TYPE REF TO /iwbep/if_cp_request_read_list,
      lo_response       TYPE REF TO /iwbep/if_cp_response_read_lst,
      lo_filter_factory TYPE REF TO /iwbep/if_cp_filter_factory,
      lt_r_number       TYPE RANGE OF ZBS_C_DemoSimpleRAP-ItemNumber.

    lo_request = mo_client_proxy->create_resource_for_entity_set( 'SimpleRap' )->create_request_for_read( ).

    lt_r_number = VALUE #( ( sign = 'I' option = 'EQ' low = 2 ) ).

    lo_filter_factory = lo_request->create_filter_factory( ).
    DATA(lo_filter_node) = lo_filter_factory->create_by_range(
      iv_property_path     = 'ITEMNUMBER'
      it_range             = lt_r_number ).
    lo_request->set_filter( lo_filter_node ).

    lo_request->set_top( 50 )->set_skip( 0 ).
    lo_response = lo_request->execute( ).

    lo_response->get_business_data( IMPORTING et_business_data = lt_business_data ).

    cl_abap_unit_assert=>assert_equals( act = lines( lt_business_data ) exp = 2 ).
  ENDMETHOD.
ENDCLASS.

 

Für die Zuweisung der Daten verwenden wir den Consumption View (Core Data Service), da hier die entsprechenden Feldnamen für den OData Service bereitgestellt werden und für die Datenbank andere Feldnamen gelten könnten.

 

Ablauf

Den entsprechenden Ablauf in der Testklasse noch einmal kurz erklärt:

  • Vorbereitung
    • Erstellung des lokalen Proxy Objekts für die Verbindung zum Service
  • Lesen von Daten
    • Erzeugen des Request Objekts zum Lesen
    • Setzen des Filters und TOP/SKIP Werte
    • Ausführen des Requests
    • Lesen der Daten aus dem Response Objekt
  • Anlegen von Daten
    • Erzeugen des neuen Datensatzes
    • Erzeugen des Request Objekts zur Anlage 
    • Übergabe der Daten
    • Ausführen des Requests
    • Auswerten der Antwort (Befüllung des Schlüssels)

 

Test Double

Du möchtest nicht gegen die echten Daten in der Datenbank testen? Dann kannst du auch einen Test Double für den Core Data Service verwenden, der angesprochen wird. Der SQL Double für die Tabelle wird hier nicht funktionieren. 

Dazu einfach in der Setup Methode das entsprechende Double vorbereiten und aktivieren. Die Zugriffe über den Proxy laufen dann gegen den CDS Double.

 

Fazit

OData Services können genau so wie andere Objekte getestet und geprüft werden, was deine Entwicklung in Zukunft stabiler machen sollte. Die Bereitstellung und Weiterentwicklung solcher Services wird in Zukunft einen großen Teil der Ressourcen ausmachen, die im Backend benötigt werden.


Enthaltene Themen:
ABAP UnitABAPUnit TestsOData testen
Kommentare (0)

ABAP Unit - Tipps

Kategorie - ABAP

Zum Abschluss der Serie noch ein paar Tipps die wir dir mit auf den Weg geben wollen. Hier geht es um Shortcuts und allgemeine Hinweise zu den Tests.

12.11.2021

ABAP Unit - Software-Architektur

Kategorie - ABAP

Wie könnte die Ziel Architektur in einem SAP System aussehen, wenn wir uns die eigenen Software Komponenten anschauen? Dies wollen wir in diesem Artikel klären.

05.11.2021

ABAP Unit - Testbarer Code (Teil 3)

Kategorie - ABAP

Hier schauen wir uns die Möglichkeiten etwas genauer an, wie man abhängige Komponenten im eigenen Coding zur Testlaufzeit deaktivieren können.

29.10.2021

ABAP Unit - Testbarer Code (Teil 2)

Kategorie - ABAP

In diesem Artikel geht es um das Thema Test Isolation und wie es unseren Code testbarer macht.

22.10.2021

ABAP Unit - Testbarer Code (Teil 1)

Kategorie - ABAP

In diesem Artikel schauen wir uns an, wie du auch in älterem Code sauber neue Funktionen implementieren und du diese im Anschluss auch testen kannst.

08.10.2021