动机

现在大家都知道单元测试对我们代码的好处。并且我们都承认它是开发过程中不可或缺的一部分。但是在把代码切换到数据库的模式下的时候,我们被粗暴地打回了软件测试的黑暗年代...我们现在面临着逻辑下推到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 Test Double Framework处理了以上的挑战,并且可以实现CDS entities测试的自动化:

  1. 在相同的Database Schema中为每个依赖组件创建临时的可更新测试替身
    1. 复制依赖组件表,但不复制任何数据。不复制依赖数据库表的主键约束。这允许你轻松地插入测试数据,而不用担心数据的完整性。相似的,数据库索引也不会被复制。
    2. 为依赖数据库视图创建数据库表。这些表有着和依赖数据库视图相同的结构。
    3. 依赖数据库functions(由带有参数的依赖CDS视图和依赖表function产生)会被复制,并且function的测试替身实现会被修改为允许插入需要的测试数据。
  2. 在相同的Database Schema创建一个CDS entity under test(CUT)的临时副本。从各种意义上来看,这个副本是为CUT服务的。在原始的CDS entity中实现的逻辑在副本中同样存在,但是依赖组件被替换为了相应的测试替身。测试替身是由我们的CDS Test Double Framework所创建的。

测试什么?

单元测试应当专注于由一个给定视图实现的有价值的功能定义。不是所有的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投影视图。

怎样使用CDS Test Double Framework写单元测试?

在下一部分,我们会通过被广泛应用的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 [] to snwd_so as _sales_order on so_guid = _sales_order.node_key
{
so_guid,
coalesce ( _sales_order.so_id, '' ) 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测试类

创建一个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 = '' 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 = '' currency_code = 'EUR' gross_amount = '' 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.

执行CDS

SELECT * FROM cdsfrwk_so_items_by_taxrate INTO TABLE @act_results.

验证输出——使用ABAP单元测试的断言

exp_results = VALUE #( ( so_id = 'ID' currency_code = 'EUR' sum_gross_amount = '' 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 = '' 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 = '' currency_code = 'EUR' gross_amount = '' 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 = '' tax_rate = '19.00' ) ).
assert_so_items_by_taxrate( exp_results = exp_results ). ENDMETHOD.

运行CDS的单元测试

ADT当中,打开包含所有CDS单元测试的ABAP测试类。右键选择Run As->ABAP Unit Test,或者使用ctrl+shift+f10组合键来运行单元测试。结果会在eclipse中的ABAP Unit Runner视图中显示。

注意:至今为止,还不能在DDL源代码编辑器中直接运行单元测试。

受支持的测试场景

CDS Test Double framework支持为给定的CUT的以下DOC创建测试替身:

  • DDIC tables
  • DDIC views
  • CDS views
  • CDS views with Parameters
  • External Views
  • Table Functions
  • CDS special functions. CURRENCY_CONVERSION and UNIT_CONVERSION

你可以打开/关闭给定CDS的DCL,更多细节会在本文的后面提供。

依赖组件是Table Function

Tables Function的测试替身的操纵方式和其它的CDS视图一样。

依赖组件是带有参数的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 = '' ) ).
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.

DCL对CUT的影响

你也可以打开/关闭给定的CDS的DCL。但是,在目前,如果你的CDS DDL在测试时受到DCL影响的话,我们建议在运行测试时总是关闭DCL。在未来,使用DCL时会有很多选项可以玩,并且可以使用角色权限测试替身等。但是在目前的版本中,你需要注意在测试CDS DDL时完全关闭DCL(如果有的话)。在打开DCL时些测试可能导致测试间断性失败,因为实际访问控制角色权限会被应用。因此,建议在你的生产测试中总是有一个:

DISABLE_DCL=ABAP_TRUE in the cl_cds_test_environment=>create(…)

对特殊function的支持:CURRENCY_CONVERSION和UNIT_CONVERSION

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 = ''
). "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值的测试

为了在测试替身中插入null值,CDS Test Double Framework提供了方法:

cl_cds_test_data=>create( .. )->set_null_values( .. )

该方法可以显式地设定null值。

partners = VALUE #( ( client = sy-mandt bp_id = '' ) ).

"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 ).

Demo examples

你可以在这个包里找到许多单元测试的例子:

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

CDS测试框架介绍:如何为ABAP CDS Entities写测试的更多相关文章

  1. spring boot应用测试框架介绍

    一.spring boot应用测试存在的问题 官方提供的测试框架spring-boot-test-starter,虽然提供了很多功能(junit.spring test.assertj.hamcres ...

  2. 『德不孤』Pytest框架 — 1、Pytest测试框架介绍

    目录 1.什么是单元测试框架 2.单元测试框架主要做什么 3.单元测试框架和自动化测试框架有什么关系 4.Pytest测试框架说明 5.Pytest框架和Unittest框架区别 (1)Unittes ...

  3. python pytest测试框架介绍三

    之前介绍了pytest以xUnit形式来写用例,下面来介绍pytest特有的方式来写用例 1.pytest fixture实例1 代码如下 from __future__ import print_f ...

  4. python pytest测试框架介绍二

    在介绍一中简单介绍了pytest的安装和简单使用,接下来我们就要实际了解pytest了 一.pytest的用例发现规则 pytest可以在不同的函数.包中发现用例,发现的规则如下 文件名以test_开 ...

  5. 【Tavern】1.tavern测试框架介绍

    目录 1. Tavern 和 Postman 2. 安装Python 3. 安装Tavern 4. 第一个测试用例 1. Tavern 和 Postman Tavern是一个用Python实现的接口自 ...

  6. MySQL Test Run 测试框架介绍

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 介绍 MySQL Test Run 简称MTR,是MySQL官方提供的自动化测试框架,执行脚本在发布路径的mysql-te ...

  7. 【转】Java学习---10个测试框架介绍

    [原文]https://www.toutiao.com/i6594302925458113027/ JAVA 程序员需要用到 10 个测试框架和库 Java 程序员需要用到十大单元测试和自动化集成测试 ...

  8. 【pytest系列】- pytest测试框架介绍与运行

    如果想从头学起pytest,可以去看看这个系列的文章! https://www.cnblogs.com/miki-peng/category/1960108.html 前言​ ​ 目前有两种纯测试的测 ...

  9. 记录项目代码迁移后,UI测试框架的搭建(配置文件的修改、测试脚本试运行)

    前文:记录一次项目代码迁移过程 上文代码迁移的目的就是为了新增vue脚手架自带的UI测试框架,工具有了,就需要实践运行在项目中了(修改配置文件.编写测试脚本等). 一.单元测试 测试框架 karma ...

随机推荐

  1. 用DOS命令来运行Java代码

    用DOS命令来运行Java代码.. ----------------- Demo.java public class Demo { public static void main(String[] a ...

  2. IDE UltraEdit 图文激活+安装教程

    IDE UltraEdit 安装+激活图文.. ---------------- ---------------- ---------------- ---------------- -------- ...

  3. DOM 对象方法

    DOM 对象方法 这里提供一些您将在本教程中学到的常用方法: 方法 描述 getElementById() 返回带有指定 ID 的元素. getElementsByTagName() 返回包含带有指定 ...

  4. DeepLearning.ai学习笔记(一)神经网络和深度学习--Week3浅层神经网络

    介绍 DeepLearning课程总共五大章节,该系列笔记将按照课程安排进行记录. 另外第一章的前两周的课程在之前的Andrew Ng机器学习课程笔记(博客园)&Andrew Ng机器学习课程 ...

  5. geotrellis使用(三十一)使用geotrellis直接将GeoTiff发布为TMS服务

    前言 传统上我们需要先将Tiff中存储的影像等数据先切割成瓦片,而后再对外提供服务.这样的好处是服务器响应快,典型的用空间来换时间的操作.然而这样造成的问题是空间的巨大浪费,一般情况下均需要存储1-1 ...

  6. Intelli IDEA学习系列之快捷键篇

    Intelli IDEA学习系列之快捷键篇 IDEA简介: IDEA 全称IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能 ...

  7. 标准的Java编码规范手册

    编码规范体现出一个开发者的基本素质,良好的编码规范可以提高团队编码的效率,避免很多不必要的问题.今天分享一个标准的Java编码规范给大家,希望对于大家今后的开发工作带来帮助. 编码规范的意义      ...

  8. log4j日志框架学习

    初识Log4j:      log4j有三个部分:           1.loggers 负责捕获日志信息.           2.appenders  负责输出信息到不同的目的地         ...

  9. Java缓存类的实际应用场景

    不要着迷于技术,应把注意力放到问题上. 一个普通的后台管理系统,一定会有参数配置.参数配置数据表和其他的数据表是不同的,它的操作基本都是查的操作.参数配置的这些数据信息是贯穿在整个项目中,那么把他们放 ...

  10. 3.修改第一个程序来点亮LED

    在上一节中已经将驱动程序框架搭建好了 接下来开始写硬件的操作(控制LED): (1)看原理图,确定引脚 (2)看2440手册 (3)写代码(需要使用ioremap()函数映射虚拟地址,在linux中只 ...