Куда пристроить модульные тесты в ABAP. Часть вторая. Первые грабли.

Первый шаг сделан. Теперь нужно расширить и углубить наше наступление. Глобальная цель – максимально полное покрытие тестами, в рамках целесообразности происходящего.

Грабля первая. Обработка ошибок.

Допустим, наш ФМ делает не замещение значений, а проверку:

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, с сахаром и гитхабом.

Отдохнём пока от грабель, надо беречь голову для следующих шишек.

Добавить комментарий

Ваш адрес email не будет опубликован.