Юніт-тести
Юніт-тест (unit test) перевіряє функціональну коректність окремого шматка коду — зазвичай методу класу. В ABAP для цього є вбудований у рантайм інструмент ABAP Unit: він запускає тести, збирає результати і показує їх в ADT. Тести пишуть і запускають самі розробники, а масові прогони виконує ABAP Test Cockpit.
Тести реалізовані як спеціальні методи локальних тестових класів (test class). Вони живуть у test include класу, не потрапляють у продуктивний код і запускаються тільки в рамках тестових прогонів.
Послідовність дій
Section titled “Послідовність дій”- Знайти у продуктивному коді залежності від зовнішніх компонентів (dependent-on components, DOC) — бази, викликів інших класів, авторизацій тощо.
- Ізолювати їх — ідеально через інтерфейс, щоб легко підмінити.
- Створити test double (мок) — руками або через фреймворк.
- Ін’єктувати double у клас, що тестується (class under test), під час тесту.
- Написати тестові класи та методи.
- Запустити тести і оцінити результат.
Тестовий клас
Section titled “Тестовий клас”Скелет:
CLASS ltc_test_class DEFINITION FOR TESTING " клас для ABAP Unit RISK LEVEL HARMLESS " HARMLESS / CRITICAL / DANGEROUS DURATION SHORT. " SHORT / MEDIUM / LONG ...ENDCLASS.
CLASS ltc_test_class IMPLEMENTATION. ...ENDCLASS.RISK LEVEL:HARMLESS— не змінює систему/дані (бажано),DANGEROUS— змінює персистентні дані,CRITICAL(default) — змінює customizing.DURATION: орієнтир часу виконання —SHORT(секунди),MEDIUM(~хв),LONG(>хв).
Доступ до private-членів
Section titled “Доступ до private-членів”Щоб тестувати private/protected методи — оголоси дружбу:
CLASS cl_class_under_test DEFINITION LOCAL FRIENDS ltc_test_class.Якщо тестових класів кілька — винеси їх на початок через DEFERRED:
CLASS ltc_test_class_1 DEFINITION DEFERRED.CLASS ltc_test_class_2 DEFINITION DEFERRED.CLASS cl_class_under_test DEFINITION LOCAL FRIENDS ltc_test_class_1 ltc_test_class_2.Інтерфейс тільки частково
Section titled “Інтерфейс тільки частково”Для тестових класів (а особливо test doubles) зручно реалізовувати інтерфейс не повністю:
CLASS ltd_test_double DEFINITION FOR TESTING. PUBLIC SECTION. INTERFACES some_intf PARTIALLY IMPLEMENTED.ENDCLASS.PARTIALLY IMPLEMENTED працює тільки у тестових класах.
Тестові методи
Section titled “Тестові методи”- Інстансові методи з доповненням
FOR TESTING. - Без параметрів.
- Викликаються фреймворком у невизначеному порядку.
- Мають бути private або protected.
CLASS ltc_test_class DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION. DATA ref_cut TYPE REF TO cl_class_under_test. " class under test METHODS some_test_method FOR TESTING.
ENDCLASS.Given-When-Then
Section titled “Given-When-Then”Структура імплементації:
- given — підготовка: створити екземпляр класу, налаштувати test double.
- when — виклик тестованого коду.
- then — перевірка результату через
CL_ABAP_UNIT_ASSERT.
METHOD some_test_method. " given ref_cut = NEW #( ).
" when DATA(result) = ref_cut->multiply_by_two( 5 ).
" then cl_abap_unit_assert=>assert_equals( act = result exp = 10 msg = |Результат множення 5 на 2 неправильний: { result }| quit = if_abap_unit_constant=>quit-no ). " не переривати тест при помилціENDMETHOD.CL_ABAP_UNIT_ASSERT
Section titled “CL_ABAP_UNIT_ASSERT”Найуживаніші асерції:
| Метод | Що перевіряє |
|---|---|
ASSERT_EQUALS | рівність двох обʼєктів |
ASSERT_DIFFERS | нерівність |
ASSERT_BOUND / ASSERT_NOT_BOUND | посилання звʼязане / не звʼязане |
ASSERT_INITIAL / ASSERT_NOT_INITIAL | обʼєкт у початковому стані |
ASSERT_SUBRC | значення sy-subrc |
ASSERT_TRUE / ASSERT_FALSE | булеве значення |
ASSERT_CHAR_CP / ASSERT_CHAR_NP | відповідність текстовому шаблону |
ASSERT_NUMBER_BETWEEN | число у діапазоні |
ASSERT_TABLE_CONTAINS / ASSERT_TABLE_NOT_CONTAINS | чи є рядок у таблиці |
ASSERT_THAT | відповідність constraint |
FAIL | примусовий провал |
SKIP | пропустити через відсутні передумови |
Основні параметри: act (фактичне), exp (очікуване), msg (повідомлення про помилку), quit (поведінка при провалі).
Методи фікстури
Section titled “Методи фікстури”Спеціальні private-методи для підготовки/прибирання:
| Метод | Коли викликається |
|---|---|
setup | перед кожним тестовим методом |
teardown | після кожного тестового методу |
class_setup | один раз перед усіма тестами (static) |
class_teardown | один раз після всіх тестів (static) |
CLASS ltc_test_class IMPLEMENTATION. METHOD setup. ref_cut = NEW #( ). " наповнити тестову БД, підготувати дані ENDMETHOD.
METHOD some_test_method. DATA(result) = ref_cut->multiply_by_two( 5 ). cl_abap_unit_assert=>assert_equals( act = result exp = 10 ). ENDMETHOD.
METHOD teardown. " відкотити зміни тестових даних ENDMETHOD.ENDCLASS.Робота з залежностями
Section titled “Робота з залежностями”Коли метод викликає БД, RFC, інший клас — ці DOC треба ізолювати і замінити на test double, щоб тест був детермінованим і швидким.
Створення test double вручну
Section titled “Створення test double вручну”- Є інтерфейс до DOC — реалізуй його у тестовому класі (див.
PARTIALLY IMPLEMENTEDвище). - Немає інтерфейсу — створи локальний, адаптуй продуктивний код під нього.
- DOC — не-final клас — успадкуйся і переозначе методи.
Ін’єкція double у тестований клас
Section titled “Ін’єкція double у тестований клас”Техніки ін’єкції:
- Constructor injection — double передається у конструктор.
- Setter injection — через сеттер.
- Parameter injection — як опційний параметр тестованого методу.
- Back door injection — через friendship: тест напряму пише у private-атрибут.
Test seams
Section titled “Test seams”Test seam — блок продуктивного коду, який можна замінити під час тесту. Корисно, коли ін’єкція неможлива — наприклад, треба замінити SELECT або AUTHORITY-CHECK.
" У продуктивному кодіTEST-SEAM select_from_db. SELECT * FROM dbtab INTO TABLE @some_table.END-TEST-SEAM." У тестовому класі — підмінаTEST-INJECTION select_from_db. some_table = VALUE #( ( ... ) ( ... ) ).END-TEST-INJECTION.Поза тестовим контекстом seam виконується як звичайний код.
Фреймворки для test doubles
Section titled “Фреймворки для test doubles”Замість ручних моків — стандартизовані фреймворки:
| DOC | Фреймворк / клас |
|---|---|
| Класи та інтерфейси | ABAP OO Test Double Framework (CL_ABAP_TESTDOUBLE) |
| CDS view entities | CDS Test Double Framework (CL_CDS_TEST_ENVIRONMENT) |
| ABAP SQL на dbtab/CDS | ABAP SQL Test Double Framework (CL_OSQL_TEST_ENVIRONMENT) |
| RAP BO — буфер | CL_BOTD_TXBUFDBL_BO_TEST_ENV |
| RAP BO — EML API | CL_BOTD_MOCKEMLAPI_BO_TEST_ENV |
Запуск у ADT
Section titled “Запуск у ADT”Ctrl+Shift+F10— запустити всі тести у класі.- Курсор на методі + запуск — тільки цей тест.
Ctrl+Shift+F11або Run as → ABAP Unit Test With… + Coverage — запуск з вимірюванням покриття. Результати — у вкладці ABAP Coverage.
Результати тестів — у вкладці ABAP Unit, деталі помилок — у Failure Trace.
Типові патерни
Section titled “Типові патерни”Тест без DOC — просто порівняти повернене значення з очікуваним через assert_equals.
Тест з мокованою базою — через CL_OSQL_TEST_ENVIRONMENT: підготувати доп. таблицю, підмінити, викликати, перевірити.
Тест методу з авторизацією — через test seam замінити AUTHORITY-CHECK на потрібний sy-subrc.
Помічник для повторюваних асерцій — винести у приватний метод тестового класу (без FOR TESTING), щоб не дублювати код перевірок.
Адаптовано з 14_ABAP_Unit_Tests.md (Apache 2.0). Повний перелік нюансів — в оригіналі.