现在大家都知道单元测试对我们代码的好处。并且我们都承认它是开发过程中不可或缺的一部分。但是在把代码切换到数据库的模式下的时候,我们被粗暴地打回了软件测试的黑暗年代...我们现在面临着逻辑下推到ABAP CDS entities后,代码要如何测试的难题。
CDS Test Double Framework允许开发者们通过众所周知的ABAP Unit Test Framework自动化地测试CDS entities。
本文链接:http://www.cnblogs.com/hhelibeb/p/7376232.html
Introduction to CDS Test Double Framework – How to write unit tests for ABAP CDS Entities?
因为CDS entity中的逻辑运行在下层的数据库中(独立于abap runtime),使用传统的ABAP依赖注入解决方案以实现测试成为了不可能的事情。entity的依赖组件需要在数据库中加上测试替身,并且我们必须确保CDS entity测试的时候数据库引擎调用/执行这些测试替身(double)。
为了可以在CDS entity under test (CUT)中可控地测试逻辑,我们需要通过测试替身注射测试专用数据。这意味着必须将测试数据插入到测试替身中,这样数据可以在CUT执行时被测试替身们返回。对于在ABAP CDS上下文中有着固有的只读属性的依赖组件(比如数据库视图和数据库函数),这是一项特别的挑战。
本文中重要的缩写:
CUT = CDS entity Under Test
DOC = Depended-On Component
CDS Test Double Framework处理了以上的挑战,并且可以实现CDS entities测试的自动化:
单元测试应当专注于由一个给定视图实现的有价值的功能定义。不是所有的CDS视图都需要一个单元测试。在实现单元测试之前,建议辨别出entity中与测试有关的方面。
通常,需要为某些包含了代码下推的方法的entity进行单元测试。潜在的测试候选者包括:
Calculations and/or filters, conversions, conditional expressions 比如 CASE…THEN…ELSE or COALESCE, type changing CAST operations, cardinality changes or checks against NULL values, JOIN behavior, complex where conditions 等.
单元测试不应用于测试那些更适用于静态检查、集成测试等技术的CDS entities属性。如果不能从单元测试中获取任何价值的话,也不应进行它,比如对于那些简单的CDS投影视图。
在下一部分,我们会通过被广泛应用的ABAP Unit Test Framework为以下的CDS视图创建单元测试。
- @AbapCatalog.sqlViewName: 'zSo_Items_By_1'
- @EndUserText.label: 'Aggregations/functions in SELECT list'
- @AbapCatalog.compiler.compareFilter: true
- define view Salesorder_Items_By_TaxRate
- as select from CdsFrwk_Sales_Order_Item
- association [1] to snwd_so as _sales_order on so_guid = _sales_order.node_key
- {
- so_guid,
- coalesce ( _sales_order.so_id, '9999999999' ) as so_id,
- currency_code,
- sum( gross_amount ) as sum_gross_amount,
- tax_rate,
- _sales_order
- }
- group by
- so_guid,
- _sales_order.so_id,
- currency_code,
- tax_rate
创建一个ABAP测试类以对CDS视图进行单元测试。有一个好的实践方法:为测试类起一个和CUT相同/相似的名字,并且加上TEST的后缀。比如,对于CDS视图Salesorder_Items_By_TaxRate,测试类的名字可以是:Salesorder_Items_By_TaxRate_Test.
因为单元测试和CDS是不同的东西,相同/相似的名字可以帮助我们轻松的寻找相关的测试。
- CLASS Salesorder_Items_By_TaxRate_Test DEFINITION FINAL FOR TESTING
- DURATION SHORT
- RISK LEVEL HARMLESS.
- PRIVATE SECTION.
- ...
- ...
- ENDCLASS.
- CLASS SO_ITEMS_BY_TAXRATE_TEST IMPLEMENTATION.
- ...
- ...
- ENDCLASS.
定义以下的安装拆卸方法。
运行方法cl_cds_test_environment=>create( i_for_entity = ‘<CDS under test>’ ),隐式地在数据库中创建所有依赖组件测试替身。这个方法在测试类中只应被调用一次。
- "Fixture method class_setup is executed only once in the beginning of the execution of test class
- METHOD class_setup.
- "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' ).
- ENDMETHOD.
- METHOD class_teardown.
- environment->destroy( ).
- ENDMETHOD.
- "Fixture method setup is executed once before each test method execution
- <cod
- METHOD setup.
- environment->clear_doubles( ).
- ENDMETHOD.
- METHOD cuco_1_taxrate_1_item_1_ok.
- ENDMETHOD.
- METHOD cuco_1_taxrate_1_item_1_ok.
- "Step 1 : Insert testdata into the doubles
- "Step 1.1 : create an instance of type snwd_so. Note : CDS view Salesorder_Items_By_TaxRate depends on snwd_so.
- sales_orders = VALUE #( ( client = sy-mandt node_key = '01' so_id = 'ID' ) ).
- "Step 1.2 : Use the framework method CL_CDS_TEST_DATA=>create(..) to create the test_data object
- test_data = cl_cds_test_data=>create( i_data = sales_orders ).
- "Step 1.3 : Use the framework method environment->get_double(..) to create the instance of the double 'SNWD_SO'
- DATA(sales_orders_double) = environment->get_double( i_name = 'SNWD_SO' ).
- "Step 1.4 : Insert the testdata into the double depended-on component object
- sales_orders_double->insert( test_data ).
- "Repeat Step 1 for all the depended-on component doubles
- sales_order_items = VALUE #( ( mandt = sy-mandt so_guid = '01' currency_code = 'EUR' gross_amount = '1' tax_rate = '19.00' ) ).
- test_data = cl_cds_test_data=>create( i_data = sales_order_items ).
- DATA(sales_order_items_double) = environment->get_double( i_name = 'CdsFrwk_DEMO_1' ).
- sales_order_items_double->insert( test_data ).
- ...
- ENDMETHOD.
- SELECT * FROM cdsfrwk_so_items_by_taxrate INTO TABLE@act_results.
- exp_results = VALUE #( ( so_id = 'ID' currency_code = 'EUR' sum_gross_amount = '1' tax_rate = '19.00' ) ).
- cl_abap_unit_assert=>assert_equals(
- act = lines( act_results )
- exp = lines( exp_results ) ).
- "The method looks as follows:
- METHOD cuco_1_taxrate_1_item_1_ok.
- "Step 1 : Insert testdata into the doubles
- "Step 1.1 : create an instance of type snwd_so
- sales_orders = VALUE #( ( client = sy-mandt node_key = '01' so_id = 'ID' ) ).
- "Step 1.2 : Use the framework method CL_CDS_TEST_DATA=>create to create the test_data object
- test_data = cl_cds_test_data=>create( i_data = sales_orders ).
- "Step 1.3 : Use the framework method environment->get_double to the instance of the DOC double 'SNWD_SO'
- DATA(sales_orders_double) = environment->get_double( i_name = 'SNWD_SO' ).
- "Step 1.4 : Insert the testdata into the DOC double object
- sales_orders_double->insert( test_data ).
- "Repeat Step 1 for all the DOC doubles
- sales_order_items = VALUE #( ( mandt = sy-mandt so_guid = '01' currency_code = 'EUR' gross_amount = '1' tax_rate = '19.00' ) ).
- test_data = cl_cds_test_data=>create( i_data = sales_order_items ).
- DATA(sales_order_items_double) = environment->get_double( i_name = 'CdsFrwk_DEMO_1' ).
- sales_order_items_double->insert( test_data ).
- "Step 2 : Execute the CDS
- SELECT * FROM cdsfrwk_so_items_by_taxrate INTO TABLE @act_results.
- "Step 3 : Verify Expected Output
- exp_results = VALUE #( ( so_id = 'ID' currency_code = 'EUR' sum_gross_amount = '1' tax_rate = '19.00' ) ).
- assert_so_items_by_taxrate( exp_results = exp_results ).
- ENDMETHOD.
在ADT当中,打开包含所有CDS单元测试的ABAP测试类。右键选择Run As->ABAP Unit Test,或者使用ctrl+shift+f10组合键来运行单元测试。结果会在eclipse中的ABAP Unit Runner视图中显示。
注意:至今为止,还不能在DDL源代码编辑器中直接运行单元测试。
CDS Test Double framework支持为给定的CUT的以下DOC创建测试替身:
你可以打开/关闭给定CDS的DCL,更多细节会在本文的后面提供。
Tables Function的测试替身的操纵方式和其它的CDS视图一样。
CDS Test Double Framework提供了
cl_cds_test_data=>create( .. )->for_parameters( .. )
来为带有参数的类型的测试替身插入数据。
- METHOD eur_tax_rate_19_found.
- "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 ).
- DATA(open_items_double) = environment->get_double( 'CdsFrwk_demo_3' ).
- open_items_double->insert( test_data ).
- ...
- ...
- ENDMETHOD.
你也可以打开/关闭给定的CDS的DCL。但是,在目前,如果你的CDS DDL在测试时受到DCL影响的话,我们建议在运行测试时总是关闭DCL。在未来,使用DCL时会有很多选项可以玩,并且可以使用角色权限测试替身等。但是在目前的版本中,你需要注意在测试CDS DDL时完全关闭DCL(如果有的话)。在打开DCL时些测试可能导致测试间断性失败,因为实际访问控制角色权限会被应用。因此,建议在你的生产测试中总是有一个:
DISABLE_DCL=ABAP_TRUE in the cl_cds_test_environment=>create(…)
CDS Test Double framework中可以为两个特殊的CDS function提供支持:
- "Step 1 : Create testdata using the special framework method create_currency_conv_data
- test_data = cl_cds_test_data=>create_currency_conv_data( output = '399.21' )->for_parameters(
- amount = '558.14'
- source_currency = 'USD'
- target_currency = 'EUR'
- exchange_rate_date = '20150218'
- ).
- "Step 2 : Get the double instance using the framework method get_double DATA(curr_conv_data_double) = environment - >get_double(cl_cds_test_environment = >currency_conversion).
- "Step 3 : Insert test_data into the double
- curr_conv_data_double->insert( test_data )."
为了在测试替身中插入null值,CDS Test Double Framework提供了方法:
cl_cds_test_data=>create( .. )->set_null_values( .. )
该方法可以显式地设定null值。
- partners = VALUE #( ( client = sy-mandt bp_id = '1' ) ).
- "Step 1 : define the list of columns into which NULL is inserted
- i_null_vals = VALUE #( ( `address_guid` ) ).
- "Step 2 : Create testdata and set the NULL value object
- test_data = cl_cds_test_data=>create( i_data = partners )->set_null_values( i_null_vals ).
- "Step 3 : Get test Double instance
- DATA(partners_double) = environment->get_double( i_name = 'SNWD_BPA' ).
- "Step 4 : Insert test data into test double
- partners_double->insert( test_data ).
你可以在这个包里找到许多单元测试的例子:
SABP_UNIT_DOUBLE_CDS_DEMO
CDS Test Double Framework从NetWeaver AS ABAP 7.51 release开始可用。
报告bug的话,请为CSS组件BC-DWB-TOO-UT-CDS创建tickets。
总结:通过本文,你现在可以使用CDS Test Double Framework高效地为你在CDS中实现的代码下推来写自动化的测试!
扩展阅读:深入探討 Test Double、Dummy、Fake、Stub 、Mock 與 Spy
来源: http://www.cnblogs.com/hhelibeb/p/7376232.html