Перейти до вмісту

Юніт-тести

Юніт-тест (unit test) перевіряє функціональну коректність окремого шматка коду — зазвичай методу класу. В ABAP для цього є вбудований у рантайм інструмент ABAP Unit: він запускає тести, збирає результати і показує їх в ADT. Тести пишуть і запускають самі розробники, а масові прогони виконує ABAP Test Cockpit.

Тести реалізовані як спеціальні методи локальних тестових класів (test class). Вони живуть у test include класу, не потрапляють у продуктивний код і запускаються тільки в рамках тестових прогонів.

  1. Знайти у продуктивному коді залежності від зовнішніх компонентів (dependent-on components, DOC) — бази, викликів інших класів, авторизацій тощо.
  2. Ізолювати їх — ідеально через інтерфейс, щоб легко підмінити.
  3. Створити test double (мок) — руками або через фреймворк.
  4. Ін’єктувати double у клас, що тестується (class under test), під час тесту.
  5. Написати тестові класи та методи.
  6. Запустити тести і оцінити результат.

Скелет:

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/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 працює тільки у тестових класах.

  • Інстансові методи з доповненням 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 — підготовка: створити екземпляр класу, налаштувати 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.

Найуживаніші асерції:

МетодЩо перевіряє
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 (поведінка при провалі).

Спеціальні 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.

Коли метод викликає БД, 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 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 виконується як звичайний код.

Замість ручних моків — стандартизовані фреймворки:

DOCФреймворк / клас
Класи та інтерфейсиABAP OO Test Double Framework (CL_ABAP_TESTDOUBLE)
CDS view entitiesCDS Test Double Framework (CL_CDS_TEST_ENVIRONMENT)
ABAP SQL на dbtab/CDSABAP SQL Test Double Framework (CL_OSQL_TEST_ENVIRONMENT)
RAP BO — буферCL_BOTD_TXBUFDBL_BO_TEST_ENV
RAP BO — EML APICL_BOTD_MOCKEMLAPI_BO_TEST_ENV
  • Ctrl+Shift+F10 — запустити всі тести у класі.
  • Курсор на методі + запуск — тільки цей тест.
  • Ctrl+Shift+F11 або Run as → ABAP Unit Test With… + Coverage — запуск з вимірюванням покриття. Результати — у вкладці ABAP Coverage.

Результати тестів — у вкладці ABAP Unit, деталі помилок — у Failure Trace.

Тест без DOC — просто порівняти повернене значення з очікуваним через assert_equals.

Тест з мокованою базою — через CL_OSQL_TEST_ENVIRONMENT: підготувати доп. таблицю, підмінити, викликати, перевірити.

Тест методу з авторизацією — через test seam замінити AUTHORITY-CHECK на потрібний sy-subrc.

Помічник для повторюваних асерцій — винести у приватний метод тестового класу (без FOR TESTING), щоб не дублювати код перевірок.

Адаптовано з 14_ABAP_Unit_Tests.md (Apache 2.0). Повний перелік нюансів — в оригіналі.