By now we have all come to recognize the advantages of unit testing our code and have accepted it as a non-negotiable part of the development process. But with the paradigm shift to push our code to the database, we have been unceremoniously blasted back to the dark ages of software testing. We are now faced with the problem of how to test our code pushdown logic implemented in ABAP CDS entities.
CDS Test Double Framework enables developers to test the logic expressed in their CDS entities in an automated way using the well-known ABAP Unit Test Framework.
Since the logic in a CDS entity is executed in the underlying database (independent of the ABAP runtime), it is not possible to use conventional ABAP solutions for dependency injection. The depended-on components (DOC) of the entity need to be doubled in the database and we have to ensure that the database engine calls/executes these doubles instead of the depended-on components when the CDS entity is tested.
In order to be able to test the logic in the CDS entity under test (CUT) in a controlled way we further have to provide a possibility to inject test-specific data into the CUT via the double. This means that it must be possible to insert test-data into the double so that this data can be returned by the doubles when the CUT is executed. This is particularly challenging for depended-on components which are inherently read-only in the context of ABAP CDS, e.g. database views and database functions.
The CDS Test Double Framework addresses the challenges above and enables unit testing of CDS entities by automatically:
Creating temporary updatable doublesfor each depended-on component in the same DB schema. In the CDS Test Double Framework, test doubles are created as stubs. Stubs provide the desired test data to the CUT. The doubles will have the same structure as the original depended-on components:
Depended-on database tables are copied without data. Primary key constraints of the depended-on database tables are not copied. This allows you to easily insert test data into the double without having to worry about data integrity. Similarly, database indexes are also not copied.
Database tables are created for depended-on database views. These tables have the same structure as the depended-on database views.
Depended-on database functions (resulting from depended-on CDS views with parameters and depended-on CDS table functions) are copied and the implementation of the function double is modified to be able to insert the desired test-data into the double.
Creating a temporary clone(copy) of the CDS entity under test (CUT) in the same DB schema. For all purposes, this clone serves as the CDS entity under test. The logic implemented in the original CDS entity is preserved in the clone but the depended-on components are replaced by the corresponding doubles that are created by the CDS Test Double Framework.
What to Test?
Unit tests should focus on checking dedicated non-trivial functions defined and implemented by a given view. Not every CDS view requires a unit test. Before implementing unit tests for CDS view it is advisable to identify the aspects of the entities which are relevant for testing.
In general, unit tests should be implemented for entities which contain some measure of code pushdown. Potential candidates for testing could be the following:
Calculations and/or filters, conversions, conditional expressions like CASE...THEN...ELSE or COALESCE, type changing CAST operations, cardinality changes or checks against NULL values, JOIN behavior, complex where conditions etc.,
Unit tests should not be used to test the properties of CDS entities which might be better tested using static checks, integration tests, etc. or if they do not provide any beneficial value like for a simple CDS projection views.
Tests which are based on the CDS Test Double Framework are not suitable for performance tests.
How to write unit tests using CDS Test Double Framework
In the following section, we will create unit tests for the following CDS view using the widely used ABAP Unit Test Framework itself.
@EndUserText.label: 'Aggregations/functions in SELECT list'
define view Salesorder_Items_By_TaxRate
as select from CdsFrwk_Sales_Order_Item
association  to snwd_so as _sales_order on so_guid = _sales_order.node_key
coalesce ( _sales_order.so_id, '9999999999' ) as so_id,
sum( gross_amount ) as sum_gross_amount,
Create an ABAP Test class
Create an ABAP Test Class to unit test the CDS view. It is a good practice to use the same/similar name of the CUT and prefix it with _TEST for the Test class.e.g. for a CDS Salesorder_Items_By_TaxRate, the test class could be Salesorder_Items_By_TaxRate_Test.
Since the unit tests and the CDS are different artifacts, same/similar names help in easily searching for the related tests.
CLASS Salesorder_Items_By_TaxRate_Test DEFINITION FINAL FOR TESTING
RISK LEVEL HARMLESS.
CLASS SO_ITEMS_BY_TAXRATE_TEST IMPLEMENTATION.
Define Fixture Methods
Define the following setup and teardown methods.
Executing the method cl_cds_test_environment=>create( i_for_entity = '<CDS under test>' ), implicitly creates all the depended-on component test doubles in the database. This method should be called only once in the test class.
"Fixture method class_setup is executed only once in the beginning of the execution of test class
"For parameter i_for_entity, specify the CDS view to be unit tested. This will create all the depended-on component Test doubles in the database.
environment = cl_cds_test_environment=>create( i_for_entity = 'Salesorder_Items_By_TaxRate' ).
In ABAP Development tools, open the ABAP Test class containing all the unit tests for CDS. Right-click and choose Run As -> ABAP Unit Test, or hit ctrl+shift+F10 to run the unit tests. The results are shown in the ABAP Unit Runner eclipse view.
Note: Running the tests being in the DDL source editor is not possible as of now.
Supported Test scenarios
CDS Test Double framework supports creation of test doubles for the following depended-on components (DOCs) for a given CDS View under test (CUT):
CDS views with Parameters
CDS special functions. CURRENCY_CONVERSION and UNIT_CONVERSION
You can also turn on/off the DCL for a given CDS. More details later are provided later in this post.
Depended-on Component is Table Function
Doubles of type Tables Functions are handled in the same manner as any CDS view
Depended-on Component is CDS View with parameters
CDS Test Double Framework offers cl_cds_test_data=>create( .. )->for_parameters( .. )
to insert test data into double of types View with parameters.
"Step 1 : Insert testdata into the doubles
open_items = VALUE #( ( mandt = sy-mandt so_guid = '0F' tax_rate = '19.00' so_id = '1' ) ).
i_param_vals = VALUE #( ( parm_name = `pCuCo` parm_value = `EUR` ) ).
"CdsFrwk_demo_3 is a CDS view with parameters. Use framework method ->for_parameters( ) to insert test data
test_data = cl_cds_test_data=>create( i_data = open_items )->for_parameters( i_param_vals ).
You can also turn on/off the DCL for a given CDS. But we recommend that for now, you always run your tests by disabling the DCL if there is a DCL acting on your CDS DDL under test. In future, there would be lot of options to play around by enabling the DCL and using the role authorization doubles etc. But with this first release, you can focus on testing the CDS DDL by disabling the DCL completely if there is one. Writing tests by enabling the DCL could result in tests failing intermittently since the actual access control role authorizations would be applied. Hence, it is always recommended to have DISABLE_DCL=ABAP_TRUE in the cl_cds_test_environment=>create(…) method for your productive tests.
Support for Special Functions : CURRENCY_CONVERSION and UNIT_CONVERSION
CDS Test Double framework offer means to create test doubles for the CDS special function CURRENCY_CONVERSION and UNIT_CONVERSION.