Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Илья Казначеев - Готовим юнит-тесты в ABAP

Илья Казначеев - Готовим юнит-тесты в ABAP

Как и зачем писать юнит-тесты в ABAP

Ilya Kaznacheev

November 22, 2018
Tweet

More Decks by Ilya Kaznacheev

Other Decks in Technology

Transcript

  1. Готовим юнит
    -
    тесты в
    ABAP
    Вкусно и полезно

    View Slide

  2. Илья
    Казначеев
    Senior ABAP Developer,
    BDO Unicon
    Founder,
    SAP Community Voronezh
    2

    View Slide

  3. Поговорим о
    ● Что такое юнит-тесты
    ● Юнит-тестирование в ABAP
    ● Тулинг
    3

    View Slide

  4. Что такое юнит-тесты
    4

    View Slide

  5. Что это такое
    Юнит-тест - автоматизированный тест изолированного от зависимостей
    логического блока кода
    5

    View Slide

  6. Для чего это нужно
    - проверка функциональности
    - проверка качества
    - фиксация поведения
    - документирование
    6

    View Slide

  7. Что тестировать
    - бизнес-логику
    - техническую логику
    - хелперы всех мастей
    - взаимодействие с legacy
    - “границы” ваших разработок
    7

    View Slide

  8. Что не тестировать
    - стандартный код
    - код, не относящийся к вашему проекту
    - сгенерированный код
    - системные вызовы и прочая низкоуровщина
    - UI, API
    - код, который будет отрефакторен
    - интеграционная логика под вопросом
    Все, что нельзя или сложно протестировать, нужно выносить в
    отдельные зависимости и тестировать все вокруг них
    8

    View Slide

  9. Test-driven development (TDD)
    9

    View Slide

  10. Юнит-тесты в ABAP
    10

    View Slide

  11. ABAP Test Cockpit
    SAP GUI
    SE80 -> ABAP Unit Browser
    SE80 -> RMB Click -> Check - ATC
    SE80 -> Shift+Ctrl+F10
    ATC
    Eclipse ADT
    Shift+Ctrl+F10
    Package tree -> RMB Click -> Run as -> ATC
    ATC in Eclipse
    11

    View Slide

  12. Как выглядит юнит-тест
    CLASS ltc_some_test DEFINITION FOR TESTING
    DURATION SHORT
    RISK LEVEL HARMLESS
    FINAL.
    PRIVATE SECTION.
    CLASS-METHODS class_setup.
    CLASS-METHODS class_teardown.
    METHODS setup.
    METHODS teardown.
    METHODS check_something FOR TESTING RAISING cx_static_check.
    METHODS check_somethingelse FOR TESTING RAISING cx_static_check.
    METHODS last_check FOR TESTING RAISING cx_static_check.
    ENDCLASS.
    12

    View Slide

  13. Что можно тестировать
    - классы
    - функциональные модули
    - CDS View
    - BOPF*
    13

    View Slide

  14. Как писать тестируемый код - теория
    - Разделение ответственностей (SoC)
    - Атомарность логического блока
    - SOLID
    - Принцип единственной ответственности (SRP)
    - Принцип открытости/закрытости (OCP)
    - Принцип подстановки Барбары Лисков (LSP)
    - Принцип разделения интерфейса (ISP)
    - Принцип инверсии зависимостей (DIP)
    - GRASP
    - А именно Low Coupling & High Cohesion
    - DRY, YAGNI и другие
    14

    View Slide

  15. Как писать тестируемый код - практика
    - добиваться единственной ответственности у класса
    - выносить зависимости во внешние объекты
    - взаимодействие с внешним миром только через явные вызовы
    - be as local as possible
    - знание класса ограничено только самим классом и интерфейсами
    взаимодействия с другими объектами
    - не бояться строить правильную архитектуру
    15

    View Slide

  16. Как писать тесты
    - тестировать только внешнее поведение класса (его интерфейс
    взаимодействия с внешним миром)
    - не тестировать реализацию класса и непубличные методы
    - тестировать только один логический объект за раз
    - тестировать только одну атомарную функцию за раз
    - писать максимально “глупые” тесты
    16

    View Slide

  17. Работа юнит-теста
    - CLASS-METHODS class_setup - один раз перед всеми тестами
    - METHODS setup - перед каждым тестом
    - METHODS teardown - после каждого теста
    - CLASS-METHODS class_teardown - один раз после всех тестов
    17

    View Slide

  18. Как тестировать legacy (и просто говнокод)
    Anti-corruption Layer - абстрактный уровень, инкапсулирующий всю
    “некрасивую” логику и предоставляющий пользователю чистый интерфейс
    взаимодействия с ней
    18

    View Slide

  19. Синтаксис - test seam
    METHOD change_price.
    DATA wa TYPE sflight.
    TEST-SEAM selection.
    SELECT SINGLE *
    FROM sflight
    INTO wa
    WHERE carrid = carrid AND
    connid = connid AND
    fldate = fldate.
    END-TEST-SEAM.
    IF sy-subrc <> 0.
    new_price = - 1.
    RETURN.
    ENDIF.
    wa-price = wa-price * factor / 100.
    TEST-SEAM modification.
    MODIFY sflight FROM wa.
    END-TEST-SEAM.
    ENDMETHOD.
    METHOD test_change_price.
    TEST-INJECTION modification.
    END-TEST-INJECTION.
    * do something
    TEST-INJECTION modification.
    sy-subrc = 4.
    END-TEST-INJECTION.
    * do something
    ENDMETHOD.
    19

    View Slide

  20. Синтаксис - local friends
    CLASS ltc_ride_handler DEFINITION DEFERRED. "local test class
    CLASS zcl_test_taxi_ride_handler DEFINITION "class under test
    LOCAL FRIENDS ltc_ride_handler.
    CLASS ltc_ride_handler DEFINITION FOR TESTING
    DURATION SHORT
    RISK LEVEL HARMLESS
    FINAL.
    * ...
    ENDCLASS.
    20

    View Slide

  21. Синтаксис - partially implemented
    CLASS mock_server DEFINITION FOR TESTING FINAL.
    PUBLIC SECTION.
    INTERFACES if_http_server PARTIALLY IMPLEMENTED.
    ENDCLASS.
    CLASS mock_request DEFINITION FOR TESTING FINAL.
    PUBLIC SECTION.
    INTERFACES if_http_request PARTIALLY IMPLEMENTED.
    ENDCLASS.
    21

    View Slide

  22. Тулинг
    22

    View Slide

  23. MockA (7.01) github.com/uweku/mockA
    DATA:
    lo_mocker TYPE REF TO zif_mocka_mocker,
    lo_mocker_method TYPE REF TO zif_mocka_mocker_method.
    lo_mocker = zcl_mocka_mocker=>zif_mocka_mocker~mock( zif_mocka_is_in_time_info=>gc_name ).
    lo_mocker_method = lo_mocker->method( 'get_delay' ).
    lo_mocker_method->with(
    i_p1 = 'LH'
    i_p2 = '300'
    i_p3 = sy-datlo
    ).
    lo_mocker_method->returns( 100 ).
    23

    View Slide

  24. ABAP Test Double Framework (7.4 SP 9)
    DATA(lo_ride) = CAST zif_test_taxi_ride(
    cl_abap_testdouble=>create(
    'zif_test_taxi_ride'
    )
    ).
    cl_abap_testdouble=>configure_call(
    lo_ride
    )->ignore_all_parameters(
    )->returning( abap_true ).
    lo_ride->request_payment( 1 ).
    24
    cl_abap_testdouble=>configure_call(
    lo_ride
    )->set_parameter(
    name = 'ev_from'
    value = VALUE geo_point( x = 1 y = 1 )
    )->set_parameter(
    name = 'ev_to'
    value = VALUE geo_point( x = 2 y = 2 )
    )->set_parameter(
    name = 'ev_rate'
    value = zif_test_taxi_ride=>basic
    )->set_parameter(
    name = 'ev_passenger_name'
    value = 'test name'
    ).
    lo_ride->get_info( ).

    View Slide

  25. Open SQL Test Double Framework (7.51)
    CLASS-DATA:
    sr_env TYPE REF TO if_osql_test_environment.
    * at the test class setup
    sr_env = cl_osql_test_environment=>create(
    VALUE #(
    ( 'MARA' )
    ( 'MAKT' )
    ( 'ZTEST_C_MATERIAL_PLANT_AGGR' )
    )
    ).
    * at the test setup
    sr_env->clear_doubles( ).
    * in test fill local tables with test data
    25
    sr_env->insert_test_data(
    i_data = lt_mara
    ).
    sr_env->insert_test_data(
    i_data = lt_marc
    ).
    sr_env->insert_test_data(
    i_data = lt_aggr
    i_parameter_values = VALUE #(
    (
    parameter_name = 'P_YEAR'
    parameter_value = '2018'
    )
    )
    ).
    *at the test class teardown
    sr_env->destroy( ).

    View Slide

  26. ABAP CDS Hierarchical Testing
    mo_environment = cl_cds_test_environment=>create(
    i_for_entity = 'P_MATVAL_MANAGE_MAX'
    i_dependency_list = VALUE #(
    ( name = 'T001K' type = 'TABLE' )
    ( name = 'MARV' type = 'TABLE' )
    )
    ).
    DATA(lo_t001k) = cl_cds_test_data=>create( i_data =
    lt_t001k ).
    lo_double = mo_environment->get_double( i_name =
    'T001K' ).
    lo_double->insert( lo_t001k ).
    DATA(l0_marv) = cl_cds_test_data=>create( i_data =
    lt_marv ).
    lo_double = mo_environment->get_double( i_name =
    'MARV' ).
    lo_double->insert( lo_marv ).
    SELECT * FROM p_matval_manage_max ...
    * execute assertations
    ABAP CDS Test Double Framework (7.51)
    ABAP CDS Unit Testing
    mo_environment = cl_cds_test_environment=>create(
    i_for_entity = 'P_MATVAL_MANAGE_MAX'
    ).
    DATA(lo_t001k) = cl_cds_test_data=>create( i_data =
    lt_t001k ).
    lo_double = mo_environment->get_double( i_name =
    'T001K' ).
    lo_double->insert( lo_t001k ).
    DATA(l0_marv) = cl_cds_test_data=>create( i_data =
    lt_marv ).
    lo_double = mo_environment->get_double( i_name =
    'MARV' ).
    lo_double->insert( lo_marv ).
    SELECT * FROM p_matval_manage_max ...
    * execute assertations
    26

    View Slide

  27. BUnit - BOPF Unit Test Framework (7.51)
    DATA(lv_date1) = sy-datum.
    DATA(lv_date2) = sy-datum - 3.
    mo_prot_ssubj->attribute(
    zif_ehs_prot_root_c=>sc_node_attribute-zehs_prot_sampl_subject-probdatefrom
    )->set( lv_date1 ).
    mo_prot_ssubj->attribute(
    zif_ehs_prot_root_c=>sc_node_attribute-zehs_prot_sampl_subject-probdateto
    )->set( lv_date2 ).
    DATA(lo_set) = /bobf/cl_bunit_node_set=>create_with_node( mo_prot_ssubj ).
    DATA(lo_result) = lo_set->execute_determination(
    zif_ehs_prot_root_c=>sc_determination-zehs_prot_sampl_subject-check_and_fill_dates
    ).
    mo_assert->node( mo_prot_ssubj )->attribute(
    zif_ehs_prot_root_c=>sc_node_attribute-zehs_prot_sampl_subject-probdatefrom
    )->equals( lv_date1 ).
    mo_assert->node( mo_prot_ssubj )->attribute(
    zif_ehs_prot_root_c=>sc_node_attribute-zehs_prot_sampl_subject-probdateto
    )->equals( lv_date1 ).
    27

    View Slide

  28. CI/CD - с миру по нитке
    Remote Code Analysis in ATC
    Program RS_AUCV_RUNNER
    28

    View Slide

  29. И че?
    29

    View Slide

  30. Ссылки
    kaznacheev.me/article/mock-obekty-na-letu-pri-
    pomoshi-atd
    kaznacheev.me/article/test-untestable
    blogs.sap.com/2015/01/05/abap-test-double-fra
    mework-an-introduction
    bit.ly/2TA63w5 - BUnit Framework
    bit.ly/2DRFqhx - ATC in details
    open.sap.com/courses/wtc1
    people.sap.com/paul.hardy2
    wiki.scn.sap.com/wiki/display/ABAP/ABAP+Unit
    +Best+Practices
    bit.ly/2KmZhWp - ABAP Unit on SAP Help
    Portal
    bit.ly/2Aax6VV - ABAP CDS Test Double
    Framework on SAP Help Portal
    habr.com/post/169381
    30

    View Slide

  31. Контакты
    kaznacheev.me
    linkedin.com/in/ilyakaznacheev
    github.com/ilyakaznacheev
    sapcmntvrn.ru
    vk.com/sapcommunityvrn
    t.me/sapcmntvrn
    31
    SAP Community Voronezh

    View Slide

  32. у вас есть минутка
    поговорить о юнит-тестах?
    32

    View Slide

  33. 33

    View Slide