Первый шаг сделан. Теперь нужно расширить и углубить наше наступление. Глобальная цель – максимально полное покрытие тестами, в рамках целесообразности происходящего.
Грабля первая. Обработка ошибок.
Допустим, наш ФМ делает не замещение значений, а проверку:
function zfi_bte_00001120.
if ls_bseg-zuonr eq space.
message ‘Поле Присвоение обязательно для заполнения’ type ‘E’.
endif.endfunction.
Тут есть две проблемы.
Во-первых, если попробовать делать прямой вызов:
call function ‘ZFI_BTE_00001120’
tables
t_bkpf = t_bkpf
t_bseg = t_bseg
t_bkpfsub = t_bkpfsub
t_bsegsub = t_bsegsub.
То обнаружится, что прогон теста падает с не очень внятным сообщением:
Exception Error <CX_AUNIT_UNCAUGHT_MESSAGE>
Можно конечно рассудить, что раз упало, следовательно была ошибка, и значит всё хорошо. Но это не так, потому что тест должен быть зелененьким, а не красненьким.
Если это было бы настоящее исключение, то можно было бы заключить вызов в конструкцию TRY-CATCH и проверить, действительно ли ловится исключение:
try.
call function ‘ZFI_BTE_00001120’.
catch CX_AUNIT_UNCAUGHT_MESSAGE.
lv_catched = ‘X’.
endtry
cl_abap_unit_assert=>assert_true( lv_catched ).
Но в данном примере исключение не настоящее, такой тест не работает. Следовательно, необходимо ловить его другим способом.
Сторонний наблюдатель, который не понимает внутреннюю кухню ABAP, мог бы заявить, что в таком случае необходимо отрефакторить сам функциональный модуль таким образом, чтобы он прямо возвращал ошибку, а не так чтобы эта ошибка бумкнула внутри него.
Это не так, и на это есть причины:
- Мы не можем как-либо поменять интерфейс этого ФМ, потому что вызываем его не мы. И мы не можем исправить место его вызова, потому что это значит “ломать стандарт”. Такая особенность у экзитов.
- Не следует вводить в ФМ технические опциональные параметры в стиле THIS_IS_TEST и TEST_RESULT, а потом это внутри ФМ делать различные действия, исходя из этих параметров. Такой костыль своё дело сделает, но очень вредно засорять продуктивный код действиями, которые нужны только для теста.
И вот оказывается, что у конструкции CALL FUNCTION есть дополнение:
… EXCEPTIONS … error_message = n_error …
Это дополнение предназначено именно для подобных случаев.
И вот мы теперь пишем тест таким образом:
call function ‘ZFI_BTE_00001120’
tables
t_bkpf = t_bkpf
t_bseg = t_bseg
t_bkpfsub = t_bkpfsub
t_bsegsub = t_bsegsub
exceptions
error_message = 99.cl_aunit_assert=>assert_subrc( act = sy-subrc exp = 99 ).
Вот теперь тест проходит правильно.
Во-вторых, из-за того что ошибка нечёткая, то в данном случае мы не можем доказать, что произошла именно нужная нам ошибка. Внутри ФМ может быть запрятано сто двадцать пять разных ошибок на разные случаи жизни. У хорошей ошибки должны быть все необходимые атрибуты: тип, класс, номер, параметры.
Значит нужно немного нарефакторить сам ФМ, причём такой рефакторинг пойдёт ему на пользу.
Было:
message ‘Поле Присвоение обязательно для заполнения’ type ‘E’.
Стало:
message e001(zfi_subst). "Поле Присвоение обязательно для заполнения
BTW: Вот это называется “ошибка повышенной чёткости”.
И после этого мы можем дополнить наш тест проверкой:
cl_aunit_assert=>assert_equals( act = sy-msgty exp = ‘E’ ).
cl_aunit_assert=>assert_equals( act = sy-msgid exp = ‘ZFI_SUBST’ ).
cl_aunit_assert=>assert_equals( act = sy-msgno exp = ‘001’ ).
BTW: в стандартной библиотеке есть много разных уточняющих смысл вариаций метода ASSERT, не видно методов, чтобы подсластить именно такую пачку. Впрочем, можно замутить свой ASSERT, с сахаром и гитхабом.
Отдохнём пока от грабель, надо беречь голову для следующих шишек.