如何对 ABAP 代码进行单元测试

举报
雨绸缪 发表于 2023/07/31 17:00:47 2023/07/31
【摘要】 ABAP 单元测试单元测试是一种专注于软件产品组件的软件测试。目的是确保每个软件代码单元按预期工作。单元可以是应用程序源代码中的函数、方法、模块、对象或其他实体。单元测试通常由开发人员在项目的编码阶段创建,并作为代码库中存在的代码编写,与它正在测试的应用程序代码一起。存在许多单元测试框架,可帮助开发人员管理和执行单元测试。单元测试的作用:确保单个软件单元(即执行具有可验证效果的代码单元)的...

ABAP 单元测试

单元测试是一种专注于软件产品组件的软件测试。目的是确保每个软件代码单元按预期工作。单元可以是应用程序源代码中的函数、方法、模块、对象或其他实体。

单元测试通常由开发人员在项目的编码阶段创建,并作为代码库中存在的代码编写,与它正在测试的应用程序代码一起。存在许多单元测试框架,可帮助开发人员管理和执行单元测试。

单元测试的作用:

  • 确保单个软件单元(即执行具有可验证效果的代码单元)的功能正确性
  • 旨在测试较大软件单元的各个组件在开发和质量保证阶段是否正常工作。通常,此类单个软件单元是方法
  • 必须由开发人员创建和运行

在 ABAP 中,开发人员可以使用 ABAP Unit(集成到 ABAP 运行时框架中的测试工具)。它可用于运行单个或批量测试,以及评估测试结果。请注意,可以使用 ABAP 测试主控室执行全面的测试运行

在 ABAP 程序中,单个单元测试作为本地测试类的测试方法实现。

创建测试类

定义:测试类是特殊的本地或全局类,其中 ABAP 单元的测试以测试方法的形式实现。在特殊测试包含的类池中创建。请参阅 ADT 中的“测试类”选项卡。只能用作测试运行的一部分。不是在生产系统中生成的,即测试类的源代码不是其程序生产代码的一部分。

测试类的框架,主要是借助于 FOR TESTING 语句,如下:

"Test class in the test include
CLASS ltc_test_class DEFINITION 
  FOR TESTING                     "Defines a class to be used in ABAP Unit
  RISK LEVEL HARMLESS             "Defines risk level, options: HARMLESS/CRITICAL/DANGEROUS
  DURATION SHORT.                 "Expected test execution time, options: SHORT/MEDIUM/LONG

  ...

ENDCLASS.  

CLASS ltc_test_class IMPLEMENTATION.
  ...
ENDCLASS.  

FOR TESTING 可用于多种用途:

  • 创建包含测试方法的测试类
  • 创建测试双精度
  • 创建帮助程序方法以支持 ABAP 单元测试

可选条件 RISK LEVEL ... :

  • CRITICAL :测试更改系统设置或自定义数据(默认)-
  • DANGEROUS :测试更改持久数据
  • HARMLESS :test 不会更改系统设置或持久数据

可选条件 DURATION ... :

  • SHORT :预计执行时间仅为几秒钟
  • MEDIUM :预计执行时间约为一分钟
  • LONG :预计执行时间超过一分钟

如何写一个单元测试

通过 ABAP 单元测试自动检查可测试的代码,我们就会说可测试的代码。因此,方法或子例程有一个干净的接口,并且对全局数据的依赖性尽可能少,即代码的干净封装。

我们先来看一个报表程序,该程序是在没有 FORM 例程的情况下经典开发的,并且仍然包含许多全局变量和相互依赖关系。我们的新实现旨在向报表添加新函数,我们希望在其中使用其他地理数据来丰富输出数据。我们希望为新功能提供单元测试。

REPORT z_test_ioh.

*----------------------------------------------------------------------*
*--- Globale data
*----------------------------------------------------------------------*
TABLES:
  t001.

TYPES:
  BEGIN OF ts_output,
    bukrs      TYPE t001-bukrs,
    butxt      TYPE t001-butxt,
    name1      TYPE adrc-name1,
    name2      TYPE adrc-name2,
    city1      TYPE adrc-city1,
    addr_group TYPE adrc-addr_group,
  END OF ts_output,
  tt_output TYPE STANDARD TABLE OF ts_output WITH EMPTY KEY.

DATA:
  gt_company_code TYPE SORTED TABLE OF t001 WITH UNIQUE KEY bukrs,
  gs_company_code TYPE t001,
  gs_address      TYPE adrc,
  gt_output_table TYPE tt_output,
  gs_output_table TYPE ts_output,
  gs_vari         TYPE disvariant,
  go_alv          TYPE REF TO cl_salv_table.

*----------------------------------------------------------------------*
*--- Selection screen
*----------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK b01.
  SELECT-OPTIONS:
    s_bukrs FOR t001-bukrs,
    s_waers FOR t001-waers.
  PARAMETERS:
    p_vari TYPE slis_vari DEFAULT '/DEFAULT',
    p_test AS CHECKBOX DEFAULT abap_true.
SELECTION-SCREEN END OF BLOCK b01.

*----------------------------------------------------------------------*
*--- Events
*----------------------------------------------------------------------*
INITIALIZATION.
  AUTHORITY-CHECK OBJECT 'S_TCODE'
   ID 'TCD' FIELD 'Z60DUMMY_TCODE'.
  IF sy-subrc <> 0.
    MESSAGE e000(z60bc) WITH 'No authority for transaction' 'Z60DUMMY_TCODE'.
  ENDIF.


AT SELECTION-SCREEN ON VALUE-REQUEST FOR p_vari.
  gs_vari-report = sy-repid.
  gs_vari-username = sy-uname.
  gs_vari-handle = 'HDL'.

  CALL FUNCTION 'REUSE_ALV_VARIANT_F4'
    EXPORTING
      is_variant = gs_vari
      i_save     = 'A'
    IMPORTING
      es_variant = gs_vari
    EXCEPTIONS
      OTHERS     = 1.

  IF sy-subrc = 0.
    p_vari = gs_vari-variant.
  ENDIF.


START-OF-SELECTION.
  CLEAR: gt_company_code, gt_output_table.

  SELECT *
    FROM t001
    WHERE bukrs IN @s_bukrs
      AND waers IN @s_waers
    INTO TABLE @gt_company_code.

  LOOP AT gt_company_code INTO gs_company_code.
    CLEAR: gs_output_table.
    gs_output_table = CORRESPONDING #( gs_company_code ).

    SELECT SINGLE *
      FROM adrc
      WHERE addrnumber = @gs_company_code-adrnr
      INTO @gs_address.

    IF sy-subrc = 0.
      gs_output_table = CORRESPONDING #( BASE ( gs_output_table ) gs_address ).
    ENDIF.

    INSERT gs_output_table INTO TABLE gt_output_table.
  ENDLOOP.

  cl_salv_table=>factory(
   IMPORTING r_salv_table = go_alv
   CHANGING t_table = gt_output_table ).

  DATA(lo_func) = go_alv->get_functions( ).
  lo_func->set_all( ).

  DATA(lo_disp) = go_alv->get_display_settings( ).
  lo_disp->set_striped_pattern( abap_true ).

  lo_disp->set_list_header( 'Found data' ).

  DATA(lo_lay) = go_alv->get_layout( ).
  lo_lay->set_default( abap_true ).
  lo_lay->set_key( VALUE #( report = sy-repid handle = 'HDL' ) ).
  lo_lay->set_save_restriction( ).

  IF p_vari IS NOT INITIAL.
    lo_lay->set_initial_layout( p_vari ).
  ENDIF.

  go_alv->display( ).

我们在全局类中实现新函数。因此,我们提供了一个接口,该接口为报表提供接口。此外,我们需要方法接口的输出结构,然后我们必须相应地宣布。我们采用界面中的结构并替换报告中的定义。我们的界面如下所示:

INTERFACE zif_test_ioh PUBLIC.
  TYPES:
    td_geometry_data TYPE p LENGTH 15 DECIMALS 8,

    BEGIN OF ts_output,
      bukrs      TYPE t001-bukrs,
      butxt      TYPE t001-butxt,
      name1      TYPE adrc-name1,
      name2      TYPE adrc-name2,
      city1      TYPE adrc-city1,
      addr_group TYPE adrc-addr_group,
      " New Fields
      latitude   TYPE td_geometry_data,
      longitude  TYPE td_geometry_data,
    END OF ts_output,
    tt_output TYPE STANDARD TABLE OF ts_output WITH EMPTY KEY,

    BEGIN OF ts_geometry,
      latitude  TYPE td_geometry_data,
      longitude TYPE td_geometry_data,
    END OF ts_geometry.

  METHODS:
    enrich_data
      CHANGING
        cs_output TYPE ts_output.
ENDINTERFACE.

CLASS zcl_test_ioh DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES zif_test_ioh.

  PROTECTED SECTION.
  PRIVATE SECTION.
    METHODS:
      get_geolocation_for_city
        IMPORTING
                  id_city            TYPE adrc-city1
        RETURNING VALUE(rs_geometry) TYPE zif_60bs_test_ioh=>ts_geometry.
ENDCLASS.

CLASS zcl_test_ioh IMPLEMENTATION.
  METHOD zif_60bs_test_ioh~enrich_data.
    DATA(ls_geometry) = get_geolocation_for_city( cs_output-city1 ).
    IF ls_geometry IS INITIAL.
      RETURN.
    ENDIF.

    cs_output-latitude = ls_geometry-latitude.
    cs_output-longitude = ls_geometry-longitude.
  ENDMETHOD.


  METHOD get_geolocation_for_city.
    " Call geolocation api
  ENDMETHOD.
ENDCLASS.
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。