$30 off During Our Annual Pro Sale. View Details »

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

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

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

Ilya Kaznacheev

November 22, 2018
Tweet

More Decks by Ilya Kaznacheev

Other Decks in Technology

Transcript

  1. Что тестировать - бизнес-логику - техническую логику - хелперы всех

    мастей - взаимодействие с legacy - “границы” ваших разработок 7
  2. Что не тестировать - стандартный код - код, не относящийся

    к вашему проекту - сгенерированный код - системные вызовы и прочая низкоуровщина - UI, API - код, который будет отрефакторен - интеграционная логика под вопросом Все, что нельзя или сложно протестировать, нужно выносить в отдельные зависимости и тестировать все вокруг них 8
  3. 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
  4. Как выглядит юнит-тест 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
  5. Как писать тестируемый код - теория - Разделение ответственностей (SoC)

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

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

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

    тестами - METHODS setup - перед каждым тестом - METHODS teardown - после каждого теста - CLASS-METHODS class_teardown - один раз после всех тестов 17
  9. Как тестировать legacy (и просто говнокод) Anti-corruption Layer - абстрактный

    уровень, инкапсулирующий всю “некрасивую” логику и предоставляющий пользователю чистый интерфейс взаимодействия с ней 18
  10. Синтаксис - 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
  11. Синтаксис - 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
  12. Синтаксис - 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
  13. 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
  14. 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( ).
  15. 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( ).
  16. 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
  17. 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
  18. Ссылки 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
  19. 33