2023 Jan 09 11:47 AM
Hello ABAPers!
Short question: How do I unit test events of an object?
Sample code: a global class that raises an event after some data was changed:
CLASS zcl_enno_001 DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
TYPES: BEGIN OF _dataline,
value TYPE string,
END OF _dataline,
_datatable TYPE SORTED TABLE OF _dataline WITH UNIQUE KEY value.
EVENTS something_changed.
METHODS add_data
IMPORTING
i_data TYPE clike.
METHODS get_data
RETURNING VALUE(r_data) TYPE _datatable.
PROTECTED SECTION.
PRIVATE SECTION.
DATA data TYPE _datatable.
ENDCLASS.
CLASS zcl_enno_001 IMPLEMENTATION.
METHOD add_data.
IF NOT line_exists( data[ value = i_data ] ).
INSERT VALUE #( value = i_data ) INTO TABLE data.
RAISE EVENT something_changed.
ENDIF.
ENDMETHOD.
METHOD get_data.
r_data = data.
ENDMETHOD.
ENDCLASS.
My (so far) unit test:
CLASS ltcl_event DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS on_something_changed
FOR EVENT something_changed OF zcl_enno_001
IMPORTING sender.
METHODS change_something FOR TESTING.
ENDCLASS.
CLASS ltcl_event IMPLEMENTATION.
METHOD change_something.
DATA(cut) = NEW zcl_enno_001( ).
cut->add_data( 'ABCD' ).
SET HANDLER on_something_changed FOR cut.
cl_abap_unit_assert=>assert_equals(
act = cut->get_data( )
exp = VALUE zcl_enno_001=>_datatable( ( value = `ABCD` ) ) ).
ENDMETHOD.
METHOD on_something_changed.
cl_abap_unit_assert=>assert_bound( sender ).
ENDMETHOD.
ENDCLASS.
Problem: the event handler method might be called, but I cannot prove with a unit test, because I cannot mark the handler method as FOR TESTING and therefore it will not be logged:
any ideas?
Thanks
~Enno
2023 Jan 09 12:43 PM
Hi enno.wulff ,
As usual with Abap Unit, I think you should only test the responsiblity of the class. Means, check the event is raised. And to catch the event, maybe you could create a Mock
I make a little test, it seems to work.
My class to be tested :
CLASS lc_class_event
DEFINITION.
PUBLIC SECTION.
EVENTS something_changed.
METHODS set_data
IMPORTING
value TYPE string
RETURNING
VALUE(success) TYPE abap_bool.
METHODS get_data
RETURNING
VALUE(result) TYPE string.
PRIVATE SECTION.
DATA gv_value TYPE string.
ENDCLASS.
CLASS lc_class_event
IMPLEMENTATION.
METHOD set_data.
gv_value = value.
RAISE EVENT something_changed.
ENDMETHOD.
METHOD get_data.
result = gv_value.
ENDMETHOD.
ENDCLASS.
The Mock of the Event Handler :
CLASS lc_class_handler_mock
DEFINITION.
PUBLIC SECTION.
METHODS catch_event_something_change
FOR EVENT something_changed OF lc_class_event.
METHODS is_something_changed_raised
RETURNING
VALUE(result) TYPE abap_bool.
PRIVATE SECTION.
DATA gv_something_changed_raised TYPE abap_bool.
ENDCLASS.
CLASS lc_class_handler_mock
IMPLEMENTATION.
METHOD catch_event_something_change.
gv_something_changed_raised = abap_true.
ENDMETHOD.
METHOD is_something_changed_raised.
result = gv_something_changed_raised.
ENDMETHOD.
ENDCLASS.
And the test class:
CLASS lc_test_event
DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS..
PRIVATE SECTION.
METHODS setup.
METHODS test_event_raised FOR TESTING.
DATA o_cut TYPE REF TO lc_class_event.
DATA o_event TYPE REF TO lc_class_handler_mock.
ENDCLASS.
CLASS lc_test_event
IMPLEMENTATION.
METHOD setup.
o_cut = NEW #( ).
o_event = NEW #( ).
SET HANDLER o_event->catch_event_something_change FOR o_cut.
ENDMETHOD.
METHOD test_event_raised.
" Given I set a new value
o_cut->set_data( `Abc` ).
" Then it should raised an event
cl_abap_unit_assert=>assert_true(
act = o_event->is_something_changed_raised( ) ).
ENDMETHOD.
ENDCLASS.
2023 Jan 09 12:43 PM
Hi enno.wulff ,
As usual with Abap Unit, I think you should only test the responsiblity of the class. Means, check the event is raised. And to catch the event, maybe you could create a Mock
I make a little test, it seems to work.
My class to be tested :
CLASS lc_class_event
DEFINITION.
PUBLIC SECTION.
EVENTS something_changed.
METHODS set_data
IMPORTING
value TYPE string
RETURNING
VALUE(success) TYPE abap_bool.
METHODS get_data
RETURNING
VALUE(result) TYPE string.
PRIVATE SECTION.
DATA gv_value TYPE string.
ENDCLASS.
CLASS lc_class_event
IMPLEMENTATION.
METHOD set_data.
gv_value = value.
RAISE EVENT something_changed.
ENDMETHOD.
METHOD get_data.
result = gv_value.
ENDMETHOD.
ENDCLASS.
The Mock of the Event Handler :
CLASS lc_class_handler_mock
DEFINITION.
PUBLIC SECTION.
METHODS catch_event_something_change
FOR EVENT something_changed OF lc_class_event.
METHODS is_something_changed_raised
RETURNING
VALUE(result) TYPE abap_bool.
PRIVATE SECTION.
DATA gv_something_changed_raised TYPE abap_bool.
ENDCLASS.
CLASS lc_class_handler_mock
IMPLEMENTATION.
METHOD catch_event_something_change.
gv_something_changed_raised = abap_true.
ENDMETHOD.
METHOD is_something_changed_raised.
result = gv_something_changed_raised.
ENDMETHOD.
ENDCLASS.
And the test class:
CLASS lc_test_event
DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS..
PRIVATE SECTION.
METHODS setup.
METHODS test_event_raised FOR TESTING.
DATA o_cut TYPE REF TO lc_class_event.
DATA o_event TYPE REF TO lc_class_handler_mock.
ENDCLASS.
CLASS lc_test_event
IMPLEMENTATION.
METHOD setup.
o_cut = NEW #( ).
o_event = NEW #( ).
SET HANDLER o_event->catch_event_something_change FOR o_cut.
ENDMETHOD.
METHOD test_event_raised.
" Given I set a new value
o_cut->set_data( `Abc` ).
" Then it should raised an event
cl_abap_unit_assert=>assert_true(
act = o_event->is_something_changed_raised( ) ).
ENDMETHOD.
ENDCLASS.
2023 Jan 09 1:00 PM