All of them need to manually mocked the data. This is most annoying work at the unit-tests.
After three days of work I get some solution to mock the data without need it to COPY + PASTE them.
In this approach all what you need it is to get the data in the preferred way (it can be SQL query, FUNCTION CALL and so one…), and save it to DB.
For example:
It can be some record from the table MARA and you know that your method should return this record. For this case you write one SQL query and save result to the Z-table. After you did it, you can always use the data from Z-table and DON’T NEED to mock MARA-data manually.
How should it works?
Two things, that we need: dynamic programming and serialization.
First thing first. We need a DB-Table to store our data. For this example we crate a table with two fields – MOCK_NAME and SERIALIZED. MOCK_NAME will be store the name of the mock object and SERIALIZED our serialized data.
Done.
In the next steps we will build something like this:
First step – we define the interface for serializable objects.
INTERFACE zif_serializable_object
PUBLIC .
INTERFACES if_serializable_object.
METHODS set_data
IMPORTING i_data TYPE any.
METHODS read_mock_data
RETURNING VALUE(r_result) TYPE REF TO data.
METHODS how_is_my_name
RETURNING VALUE(r_result) TYPE zmock_name.
ENDINTERFACE.
Done.
To avoid the dependencies in the future we create also an interface for creator. The class that implement this interface can CRUD operation (Create, Read, Update, Delete)
INTERFACE zif_mock_creater
PUBLIC .
METHODS: create_mock_obj
IMPORTING i_mock_name TYPE zmock_name
RETURNING VALUE(r_result) TYPE REF TO zif_serializable_object.
METHODS: read_mock_obj
IMPORTING i_mock_name TYPE zmock_name
RETURNING VALUE(r_result) TYPE REF TO zif_serializable_object.
METHODS: update_mock_obj
IMPORTING i_mock TYPE REF TO zif_serializable_object
i_data TYPE any
RETURNING VALUE(r_result) TYPE REF TO zif_serializable_object.
METHODS: delete_mock_obj
IMPORTING i_mock_name TYPE zmock_name
RETURNING VALUE(r_result) TYPE sy-subrc.
ENDINTERFACE.
Now its time to implement serializable class.
CLASS zcl_mock DEFINITION
PUBLIC
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES zif_serializable_object.
METHODS constructor
IMPORTING
i_mock_name TYPE zmock_name.
PROTECTED SECTION.
PRIVATE SECTION.
DATA: mock_name TYPE zmock_name,
mock_data TYPE REF TO data.
ENDCLASS.
CLASS zcl_mock IMPLEMENTATION.
METHOD constructor.
me->mock_name = i_mock_name.
ENDMETHOD.
METHOD zif_serializable_object~how_is_my_name.
r_result = mock_name.
ENDMETHOD.
METHOD zif_serializable_object~set_data.
DATA: type_name TYPE string.
DATA(type) = cl_abap_typedescr=>describe_by_data( i_data ).
CASE type->kind.
WHEN cl_abap_typedescr=>kind_intf.
WHEN cl_abap_typedescr=>kind_class.
WHEN cl_abap_typedescr=>kind_elem.
DATA(type_descr) = cl_abap_elemdescr=>describe_by_data( i_data ).
type_name = type_descr->get_relative_name( ).
CREATE DATA mock_data TYPE (type_name).
WHEN cl_abap_typedescr=>kind_struct.
DATA(struc_descr) = CAST cl_abap_structdescr( cl_abap_datadescr=>describe_by_data( i_data ) ).
type_name = struc_descr->get_relative_name( ).
CREATE DATA mock_data TYPE (type_name).
WHEN cl_abap_typedescr=>kind_table.
DATA(table_descr) = CAST cl_abap_tabledescr( cl_abap_tabledescr=>describe_by_data( i_data ) ).
DATA(line_decr) = table_descr->get_table_line_type( ).
type_name = line_decr->get_relative_name( ).
TYPES ty_sflight_lines TYPE STANDARD TABLE OF sflight WITH EMPTY KEY.
CREATE DATA mock_data TYPE ty_sflight_lines.
WHEN cl_abap_typedescr=>kind_ref.
WHEN OTHERS.
RETURN.
ENDCASE.
FIELD-SYMBOLS <dref> TYPE any.
ASSIGN me->mock_data->* TO <dref>.
<dref> = i_data.
ENDMETHOD.
METHOD zif_serializable_object~read_mock_data.
r_result = mock_data.
ENDMETHOD.
ENDCLASS.
How you can see this method implement zif_serializable_object interface. The most interesting method in this class is set _data(). There I check for the type of the input parameter and then assign it to mock_data attribute.
Last class in this chain is creator class.
CLASS zcl_mock_creator DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES zif_mock_creater.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_mock_creator IMPLEMENTATION.
METHOD zif_mock_creater~create_mock_obj.
r_result = NEW zcl_mock( i_mock_name ).
ENDMETHOD.
METHOD zif_mock_creater~read_mock_obj.
TRY.
DATA(deserialized_obj) = NEW zcl_mock_serializer( )->deserialize( i_mock_name ).
CATCH zcx_mock_err INTO DATA(exc).
ENDTRY.
r_result = deserialized_obj.
ENDMETHOD.
METHOD zif_mock_creater~update_mock_obj.
i_mock->set_data( i_data ).
DATA(serialized_mock) = NEW zcl_mock_serializer( )->serialize( i_mock ).
DATA(ser_line) = VALUE zmock_serialized( mock_name = i_mock->how_is_my_name( )
serialized = serialized_mock ).
MODIFY zmock_serialized FROM ser_line.
COMMIT WORK.
ENDMETHOD.
METHOD zif_mock_creater~delete_mock_obj.
DELETE FROM zmock_serialized WHERE mock_name = i_mock_name.
r_result = sy-subrc.
ENDMETHOD.
ENDCLASS.
In this class we define the methods for Create, Read, Update and Delete functions.
ZCL_MOCK_SERIALIZER just a helper that serialize and deserialize the object. The date which use ZCL_MOCK_SERIALIZER has the XSTRING type. The same type we use in our DB-Table to save the object.
CLASS zcl_mock_serializer DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
METHODS serialize
IMPORTING i_object TYPE REF TO zif_serializable_object
RETURNING VALUE(r_result) TYPE xstring.
METHODS deserialize
IMPORTING i_mock_name TYPE zmock_name
RETURNING VALUE(r_result) TYPE REF TO zif_serializable_object
RAISING zcx_mock_err.
PROTECTED SECTION.
PRIVATE SECTION.
ENDCLASS.
CLASS zcl_mock_serializer IMPLEMENTATION.
METHOD serialize.
CALL TRANSFORMATION id SOURCE obj = i_object RESULT XML DATA(xstring)
OPTIONS data_refs = 'heap-or-create'.
r_result = xstring.
ENDMETHOD.
METHOD deserialize.
SELECT SINGLE serialized
FROM zmock_serialized
INTO @DATA(xstring)
WHERE mock_name = @i_mock_name.
IF xstring IS INITIAL.
RAISE EXCEPTION TYPE zcx_mock_err
EXPORTING
textid = zcx_mock_err=>dyn_mess
param1 = 'Keinen Serislized objekt wurde gefunden'.
ENDIF.
TRY.
DATA deserialized_object TYPE REF TO zif_serializable_object.
CALL TRANSFORMATION id SOURCE XML xstring RESULT obj = deserialized_object.
CATCH cx_xslt_runtime_error.
ROLLBACK WORK.
RETURN.
ENDTRY.
r_result = deserialized_object.
ENDMETHOD.
ENDCLASS.
Now is interesting part. So looks a test program:
We can choose any type to select and save the mock data.
REPORT.
FIELD-SYMBOLS <dref> TYPE any.
SELECTION-SCREEN BEGIN OF BLOCK b1 WITH FRAME TITLE title.
SELECTION-SCREEN BEGIN OF LINE.
PARAMETERS: p_rb1 RADIOBUTTON GROUP grp1 DEFAULT 'X'.
SELECTION-SCREEN COMMENT (15) lbl1 FOR FIELD p_rb1.
PARAMETERS: p_rb2 RADIOBUTTON GROUP grp1.
SELECTION-SCREEN COMMENT (15) lbl2 FOR FIELD p_rb2.
PARAMETERS: p_rb3 RADIOBUTTON GROUP grp1.
SELECTION-SCREEN COMMENT (15) lbl3 FOR FIELD p_rb3.
SELECTION-SCREEN END OF LINE.
SELECTION-SCREEN END OF BLOCK b1.
INITIALIZATION.
title = 'Datentyp auswählen'.
lbl1 = 'Integer:'.
lbl2 = 'Structure:'.
lbl3 = 'Table:'.
START-OF-SELECTION.
DATA(mock_builder) = NEW zcl_mock_creater( ).
* Any data type
CASE 'X'.
WHEN p_rb1.
DATA numb TYPE i VALUE 5.
WHEN p_rb2.
SELECT SINGLE * FROM MARA INTO @DATA(mara_record).
WHEN p_rb3.
SELECT * FROM sflight UP TO 10 ROWS INTO TABLE @DATA(sflight_tab).
ENDCASE.
* 1 Create
DATA(mock) = mock_builder->zif_mock_creater~create_mock_obj( 'MY_MOCK_OBJECT' ).
* 2 Update
CASE 'X'.
WHEN p_rb1.
mock_builder->zif_mock_creater~update_mock_obj( i_mock = mock i_data = numb ).
WHEN p_rb2.
mock_builder->zif_mock_creater~update_mock_obj( i_mock = mock i_data = mara_record ).
WHEN p_rb3.
mock_builder->zif_mock_creater~update_mock_obj( i_mock = mock i_data = sflight_tab ).
ENDCASE.
* 3 Read de-serialized from DB
DATA(mock_data) = mock_builder->zif_mock_creater~read_mock_obj( 'MY_MOCK_OBJECT' )->read_mock_data( ).
ASSIGN mock_data->* TO <dref>.
* 4 Delete
mock_builder->zif_mock_creater~delete_mock_obj( 'MY_MOCK_OBJECT' ).
If you start the program, you can see that is no matter which data we want to save into DB. After read data from DB the helper class serialize the data and it will be saved into DB. After that we can read the data and get the same format of the data that we save into Z-table (MARA-Record for example). This data type we can use in our unit test. Is it not a great?!
No comments:
Post a Comment