thoughts about the ABAP SQL Test Double Framework
Dear community, in this blog I’ve told you about my first experiences with the ABAP SQL Test Double Framework. It allows the creation of database table test doubles that contain defined records for unit tests. I’m still enthusiastic and I consider this technology as an important component to ensure code quality. Here are some thoughts I’ve had since then.
In general, I think it makes sense to use the same language among colleagues. This makes it easier to understand what the dialog partner means. Especially for complex test data, it could be a good idea to assign a synonym to special data records. For me, the behavior of a method is often of interest by one, two and many data records. Here are some proposals:
I thought about using the primary key to identify a special record in a dialog. That would work for classic database tables (“let’s talk about purchase order 0045008022“) but that would be no fun if a GUID is key.
Provide Test Double entries
In my experience, test data should be defined together. This creates different data constellations. I often exchange ideas with consultant colleagues. We look together at database tables and their content and talk about possible test cases. It would be great to have a tool with which you can mark a data record in transaction SE16, for example, and that generates the ABAP INSERT-statement with all the fields and values of this “real” record. Think of a database table like EKKO or VBAK, there are a lot of fields. Is there something like this available?
In order to provide special test data in a uniform way, there should be a provider class per use case. As a result, the provision of the test data records is standardized, carried out at only one point in the application logic and can be reused. Using the example from my former blog, it could look like this.
CLASS zcl_system_clients_asql_td DEFINITION PUBLIC FINAL CREATE PUBLIC. PUBLIC SECTION. METHODS constructor. METHODS provide_test_double_records RETURNING VALUE(result) TYPE zcl_system_clients=>clients. PROTECTED SECTION. PRIVATE SECTION. DATA test_double_records TYPE zcl_system_clients=>clients. METHODS add_golden_record. METHODS add_silver_record. METHODS add_bronze_record. ENDCLASS. CLASS zcl_system_clients_asql_td IMPLEMENTATION. METHOD constructor. add_golden_record( ). add_silver_record( ). add_bronze_record( ). ENDMETHOD. METHOD provide_test_double_records. result = test_double_records. ENDMETHOD. METHOD add_golden_record. INSERT VALUE #( mandt = '000' mtext = 'golden record' ) INTO TABLE test_double_records. ENDMETHOD. METHOD add_silver_record. INSERT VALUE #( mandt = '001' mtext = 'silver record' ) INTO TABLE test_double_records. ENDMETHOD. METHOD add_bronze_record. INSERT VALUE #( mandt = '999' mtext = 'bronze record' ) INTO TABLE test_double_records. ENDMETHOD. ENDCLASS.
If you add a GET-method per record, every developer can check what data is provided for golden/silver/bronze records. Below is the refactored test class.
CLASS ltc_test DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS. PUBLIC SECTION. METHODS get_client_000 FOR TESTING. METHODS get_client_001 FOR TESTING. METHODS get_client_999 FOR TESTING. PRIVATE SECTION. CLASS-DATA osql_test_environment TYPE REF TO if_osql_test_environment. CLASS-DATA clients_test_data_provider TYPE REF TO zcl_system_clients_asql_td. CLASS-METHODS class_setup. CLASS-METHODS class_teardown. ENDCLASS. CLASS ltc_test IMPLEMENTATION. METHOD class_setup. osql_test_environment = cl_osql_test_environment=>create( VALUE #( ( 'T000' ) ) ). clients_test_data_provider = NEW zcl_system_clients_asql_td( ). DATA(clients_test_data) = clients_test_data_provider->provide_test_double_records( ). osql_test_environment->insert_test_data( clients_test_data ). ENDMETHOD. METHOD class_teardown. osql_test_environment->destroy( ). ENDMETHOD. METHOD get_client_000. DATA(cut) = NEW zcl_system_clients( ). DATA(result) = cut->get_client( '000' ). cl_aunit_assert=>assert_not_initial( result ). ENDMETHOD. METHOD get_client_001. DATA(cut) = NEW zcl_system_clients( ). DATA(result) = cut->get_client( '001' ). cl_aunit_assert=>assert_not_initial( result ). ENDMETHOD. METHOD get_client_999. DATA(cut) = NEW zcl_system_clients( ). DATA(result) = cut->get_client( '999' ). cl_aunit_assert=>assert_not_initial( result ). ENDMETHOD. ENDCLASS.
That were my thoughts. In practice, I have not yet been able to try any of these. I’m still at the beginning 🙂 Whats your opinion?
Best regards, thanks for reading and please stay healthy
P.S.: Please support the virtual wishing well.