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)

745

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.



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 Unit
Comments (0)



And further ...

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


RAP - Translation app (example)

Category - ABAP

Let's take a look at a practical example of developing a RAP application in the ABAP environment and how you can create an app with little effort.

08/02/2024

RAP - Custom Entity Value Help (Deep Dive)

Category - ABAP

With the Custom Entity you have the most freedom in RAP when developing ABAP Cloud applications, but what about potential errors?

07/12/2024

RAP - Deep Action in OData v4

Category - ABAP

In this article we will look at actions with deep structures, how we can create them and pass data to an API endpoint.

05/24/2024

BTP - Connect On-Premise (Consumption Model v2)

Category - ABAP

In this article we want to provide another update on the connection of on-premise systems and how this is done with a communication arrangement.

12/15/2023

RAP - Show app count (Tile)

Category - ABAP

This example is about displaying a counter on the tile of a Fiori Elements application and how such a thing can be implemented.

10/06/2023