This is a test message to test the length of the message box.
Login
|
ABAP RAP ABAP Unit
Created by Software-Heroes

RAP - ABAP Unit (Service)

2317

How do you actually test the service of an interface or RAP app automatically? In this article we look at unit tests for OData services.

Advertising


In the last article we looked at ABAP Unit for the RAP Business Object and how you can use it to test the various functions automatically. Today we look at automating the tests for the OData service and how you can best carry out the implementation.

 

Write test

In the first step, we start again with the test class wrapper, using a global class for the test, which we connect to the service we are testing via ABAP Doc. The shell of the class looks like this:

"! @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.

 

We define three tests to validate the functionality of the service:

  • Read all records
  • Adaptation of a data set
  • Delete a record

 

However, before we start testing, we need a test client for each test. Here you have the option to create one for OData V2 or V4. Since we want to test our V2 service, the creation looks like this, passing the service and the version:

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

 

Hint: You can use the class to create different types of clients, the local proxy is required for the tests.

 

Read

After we have created a test client, we create a read request on the partner entity, execute it without a filter and get the result back. We check whether the table is filled and whether there is a certain entry:

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' ] ).

 

Update

In order to update a data record, we create an update request via the client. To do this, we have to enter the key after the entity and select the PUT operation. We then transfer the new data and execute the request. At the end we check against the database whether the record has actually been changed.

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' ).

 

Delete

Deleting works similarly to updating, except that at the end we create a delete request and don't pass any data. At the end we check if the record has been deleted from the database.

    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 ).

 

Hint: Depending on which features you have implemented, update or delete will not work in the test. Without an implemented feature control, the tests work as usual.

 

Mock data

We are currently using the real data from the database for the tests, but this does not speak for repeatable tests. Therefore we should mock the data so that we use the same data for each test and thus also ensure that the data is always available and in the state that we need. To do this, we define the following variables and methods in the PRIVATE section:

CLASS-DATA:
  go_environment TYPE REF TO if_cds_test_environment.

CLASS-METHODS:
  class_setup RAISING cx_static_check,
  class_teardown.

 

Then we can implement CLASS_SETUP, in this case we create a CDS double for the consumption view, since the service works on this view. The table is also supplied with its data as a dependent object. Finally, we activate the double so that the data is drawn correctly:

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

 

In the CLASS_TEARDOWN we then take care that the CDS double is also removed cleanly:

go_environment->destroy( ).

 

The data is now stored cleanly for each test case and you don't have to worry about what the data looks like in the database. At the end you always get the current test data for your test cases.

 

Complete example

The full example is back here at the end of the article and includes the complete test class:

"! @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.

 

Conclusion

In addition to testing the RAP object, you can now also test the service and populate it with test data without working on the real data. In the RAP environment, there are these two types of tests (business object and service).


Included topics:
RAPBTPABAP UnitREX1
Comments (3)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Tuesday and Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


RAP - Hybrid Pattern

Category - ABAP

Let's take a look at the hybrid pattern in the development of RAP objects. Who is this scenario intended for, how does it work, and what basic components are needed?

06/19/2026

BTP - Connect On-Prem (SQL Service)

Category - ABAP

How can we connect an on-premise SQL service to our ABAP environment, and what advantages does this offer? Let's take a technical deep dive.

05/29/2026

RAP - Implement Change Documents (native)

Category - ABAP

If you have the appropriate release, you can now implement change documents natively in RAP without much manual implementation. Let's look at the different steps.

04/24/2026

RAP - Auxiliary Class

Category - ABAP

As the implementation grows in the behavior implementation of a RAP object, what options do you still have for clean encapsulation? Let's look at this in detail.

04/17/2026

RAP - Implement Change Documents (Manual)

Category - ABAP

This article delves into the manual implementation of change documents in our RAP object and examines the various integration steps. The goal is to generate change documents automatically.

04/14/2026