Меню

Библиотека zsql_test_double_framework для эффективного создания unit-тестов логики взаимодействия с СУБД, начиная с версии 7.02

Стандартный инструмент ABAP Open SQL Test Double Framework, несомненно, является одним из самых мощных и удачных для тестирования логики взаимодействия с СУБД. К сожалению, данный фреймворк поддерживается только в новых версиях, начиная с 7.51. Для версий с 7.02 по 7.50 предлагается использовать аналог – библиотеку zsql_test_double_framework, которая обладает схожими возможностями и также позволяет писать unit-тесты для программ, работающих с СУБД.

Введение

Unit-тесты играют большую роль в процессе современной разработки ПО. Они позволяют проверять код на правильность очень быстро, зачастую за секунды. Это достигается изоляцией unit-тестов от любых внешних зависимостей, таких как сеть, файловая система или база данных.

Большинство ABAP-разработок используют СУБД, поэтому техника изоляции зависимости от базы данных является крайне важной для эффективного написания тестов.

ABAP SQL Test Double Framework, с моей точки зрения, – одна из лучших реализаций, о которой можно было только мечтать. Данный фреймворк позволяет создавать виртуальную базу данных со своими данными, никак не связанными с реальной СУБД. Программа продолжает работать как и раньше, с использованием инструкций Open SQL, но при этом работает с виртуальной СУБД, и мы можем использовать это в наших unit-тестах.

Пример работы с фреймворком можно посмотреть, например, здесь.

К сожалению, поддержка данного фреймворка начинается только с версии SAP BASIS 7.51, что недоступно для многих проектов. Например, на моём текущем проекте используется SAP ERP EHP 8, а также SAP Solution Manager, всё обновлено до самых «свежих» версий, но максимальная версия базиса у нас лишь 7.50. Поэтому тестировать базу данных с использованием данного инструмента мы на своём проекте не можем.

Проект zsql_test_double_framework

Для того чтобы получить возможности, схожие со стандартным фреймворком ABAP SQL Test Double Framework, но на версиях базиса 7.50 и ниже, я решил написать свою Z-разработку, которую я в итоге оформил как open source проект, и он доступен для использования всеми желающими.

Проект называется zsql_test_double_framework. Прочитать документацию и скачать проект для загрузки через ABAP Git можно по этой ссылке:

https://github.com/raaleksandr/zsql_test_double_framework

При создании проекта я преследовал следующие цели:

  • Поддержка синтаксиса Open SQL.
  • Сведение к минимуму необходимости адаптации имеющегося кода, написанного без учёта необходимости написания тестов.
  • Поддержка основных конструкций, доступных в Open SQL для взаимодействия с ABAP-кодом – использование переменных как в условиях, так и для получения значений, поддержка for all entries, подзапросы и т.д.

Основное неудобство, которое придётся испытать при использовании библиотеки, заключается в необходимости переписывать текст SQL-запросов из статического в динамический вид, передав его через текстовую строку. Из-за того, что основная часть синтаксиса Open SQL поддерживается библиотекой, чаще всего будет достаточно обрамить имеющийся SQL-запрос в кавычки и сделать ряд несложных манипуляций.

В следующем разделе рассмотрим пример.

Пример написания тестов на отчёт

Предположим, что у нас есть отчёт, который выбирает данные из базы данных и выводит в ALV-грид.

Приведём код исходного отчёта до того, как мы напишем unit-тесты. SQL-запрос здесь написан в обычном виде через open sql.

TYPES: BEGIN OF ty_grid_line,

         carrname   TYPE scarr-carrname,

         countryfr  TYPE spfli-countryfr,

         cityfrom   TYPE spfli-cityfrom,

         airpfrom   TYPE spfli-airpfrom,

         countryto  TYPE spfli-countryto,

         cityto     TYPE spfli-cityto,

         airpto     TYPE spfli-airpto,

         fldate     TYPE sflight-fldate,

         price      TYPE sflight-price,

         currency   TYPE sflight-currency,

         paymentsum TYPE sflight-paymentsum,

       END OF ty_grid_line.



TYPES ty_grid TYPE STANDARD TABLE OF ty_grid_line WITH KEY carrname.



DATA: carrid TYPE scarr-carrid,

      fldate TYPE sflight-fldate.



SELECT-OPTIONS: s_carrid FOR carrid,

                s_fldate FOR fldate.



CLASS lcl_database_reader DEFINITION.

  PUBLIC SECTION.

    METHODS: read_data IMPORTING it_select_carrid TYPE typ_r_carrid

                                 it_select_fldate TYPE typ_r_fldate

                       EXPORTING et_data          TYPE ty_grid.

ENDCLASS.



CLASS lcl_application DEFINITION.

  PUBLIC SECTION.

    METHODS: start_of_selection.

ENDCLASS.



CLASS lcl_database_reader IMPLEMENTATION.

  METHOD read_data.



    SELECT scarr~carrname

           spfli~countryfr

           spfli~cityfrom

           spfli~airpfrom

           spfli~countryto

           spfli~cityto

           spfli~airpto

           sflight~fldate

           sflight~price

           sflight~currency

           sflight~paymentsum

      FROM sflight

      JOIN scarr ON scarr~carrid = sflight~carrid

      JOIN spfli ON spfli~carrid = sflight~carrid

                AND spfli~connid = sflight~connid

      INTO CORRESPONDING FIELDS OF TABLE et_data

      WHERE sflight~carrid IN it_select_carrid

        AND sflight~fldate IN it_select_fldate.

  ENDMETHOD.

ENDCLASS.



CLASS lcl_application IMPLEMENTATION.

  METHOD start_of_selection.

    DATA: lo_reader        TYPE REF TO lcl_database_reader,

          lt_data_for_grid TYPE ty_grid,

          lo_alv           TYPE REF TO cl_salv_table,

          lo_error         TYPE REF TO cx_root,

          lv_error_text    TYPE string.



    CREATE OBJECT lo_reader.

    lo_reader->read_data( EXPORTING it_select_carrid = s_carrid[]

                                    it_select_fldate = s_fldate[]

                          IMPORTING et_data          = lt_data_for_grid ).



    TRY.

        cl_salv_table=>factory( IMPORTING r_salv_table = lo_alv

                                CHANGING  t_table      = lt_data_for_grid ).



        lo_alv->display( ).

      CATCH cx_root INTO lo_error.

        lv_error_text = lo_error->get_text( ).

        MESSAGE lv_error_text TYPE 'I' DISPLAY LIKE 'E'.

    ENDTRY.

  ENDMETHOD.

ENDCLASS.



START-OF-SELECTION.

  PERFORM start_of_selection.

*&---------------------------------------------------------------------*

*& Form START_OF_SELECTION

*&---------------------------------------------------------------------*

*& Entry point

*&---------------------------------------------------------------------*

*& -->  p1        text

*& <--  p2        text

*&---------------------------------------------------------------------*

FORM start_of_selection .

  DATA: lo_application  TYPE REF TO lcl_application.



  CREATE OBJECT lo_application.

  lo_application->start_of_selection( ).

ENDFORM.

Получившийся результат выводится в грид, как на рисунке 1.

Рис. 1. Результат отчёта в виде ALV-грида.

Перепишем отчёт, чтобы он работал через zsql_test_double_framework, а потом напишем на него unit-тест.

TYPES: BEGIN OF ty_grid_line,
         carrname   TYPE scarr-carrname,
         countryfr  TYPE spfli-countryfr,
         cityfrom   TYPE spfli-cityfrom,
         airpfrom   TYPE spfli-airpfrom,
         countryto  TYPE spfli-countryto,
         cityto     TYPE spfli-cityto,
         airpto     TYPE spfli-airpto,
         fldate     TYPE sflight-fldate,
         price      TYPE sflight-price,
         currency   TYPE sflight-currency,
         paymentsum TYPE sflight-paymentsum,
       END OF ty_grid_line.

TYPES ty_grid TYPE STANDARD TABLE OF ty_grid_line WITH KEY carrname.

DATA: carrid TYPE scarr-carrid,
      fldate TYPE sflight-fldate.

SELECT-OPTIONS: s_carrid FOR carrid,
                s_fldate FOR fldate.

CLASS lcl_database_reader DEFINITION.
  PUBLIC SECTION.
    METHODS: constructor IMPORTING io_db_layer TYPE REF TO zif_zosql_db_layer OPTIONAL,
      read_data IMPORTING it_select_carrid TYPE typ_r_carrid OPTIONAL
                          it_select_fldate TYPE typ_r_fldate OPTIONAL
                EXPORTING et_data          TYPE ty_grid
                RAISING   zcx_zosql_error.

  PRIVATE SECTION.
    DATA: go_db_layer  TYPE REF TO zif_zosql_db_layer.
ENDCLASS.

CLASS lcl_application DEFINITION.
  PUBLIC SECTION.
    METHODS: start_of_selection.
ENDCLASS.

CLASS lcl_database_reader IMPLEMENTATION.

  METHOD constructor.
    IF io_db_layer IS BOUND.
      go_db_layer = io_db_layer.
    ELSE.
      go_db_layer = zcl_zosql_test_environment=>get_db_layer_for_production( ).
    ENDIF.
  ENDMETHOD.

  METHOD read_data.

    DATA: ls_param  TYPE zosql_db_layer_param,
          lt_params TYPE zosql_db_layer_params,
          lv_select TYPE string.

    ls_param-param_name_in_select = ':it_select_carrid'.
    zcl_zosql_utils=>move_corresponding_table( EXPORTING it_table_src  = it_select_carrid
                                              

Если хотите прочитать статью полностью и оставить свои комментарии присоединяйтесь к sapland

У вас уже есть учетная запись?

Войти