我有一个场景,我的类方法(a(调用另一个类方法(B(。所以A依赖于B。我想摆脱依赖,以便能够运行单元测试。解耦和依赖注入在某种程度上是基于实例化的。但是类方法(静态方法(本质上不需要实例。我已经制定了两个可行的解决方案,但对我来说,没有一个是100%的:
- 我们创建类B的实例(生产代码(或测试double的实例(用于单元测试(。我们将它作为一个参数注入到测试中的类方法中。内部类方法是在注入的实例上调用的,而不是在类上调用的
我不喜欢我们需要创建类的实例,尽管我们使用类方法。这可能需要时间。它需要更多的代码。
- 我们注入实际类名作为字符串参数,并使用动态
CALL METHOD
由于我不喜欢解释语言,我认为这是一个混乱的可能带来严重的运行时问题。既然我们这样做是为了实现单元测试,从而消除可能的错误;使用动态调用似乎适得其反此外,使用参数也很痛苦。
有其他方法可以解决这个问题吗?我遗漏了什么重要的一点吗
波纹管,这两种解决方案都有关键部分。理解这个问题并不重要,但它们可能会有所帮助。
1(
INTERFACE lif_readfile.
CLASS-METHODS gui_upload
IMPORTING file_path TYPE string
CHANGING data_tab TYPE truxs_t_text_data.
ENDINTERFACE.
CLASS lcl_file_operations DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
get_file_length
IMPORTING
!file_path TYPE string
CHANGING
!filereader TYPE REF TO lif_readfile OPTIONAL
RETURNING
VALUE(text_length) TYPE i.
ENDCLASS.
CLASS lcl_file_operations IMPLEMENTATION.
METHOD get_file_length.
*create instance of default productive class
IF filereader IS NOT BOUND.
filereader = NEW lcl_readfile( ).
ENDIF.
*use instance to call class method
filereader->gui_upload(
EXPORTING file_path = file_path
CHANGING data_tab = lt_data
).
*code under test here..
ENDMETHOD.
ENDCLASS.
2(
CLASS lcl_file_operations DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
get_file_length
IMPORTING
!file_path TYPE string
!classname TYPE string DEFAULT 'LCL_READFILE'
RETURNING
VALUE(text_length) TYPE i.
ENDCLASS.
CLASS lcl_file_operations IMPLEMENTATION.
METHOD get_file_length.
*parameter definition
ptab = VALUE #( ( name = 'FILE_PATH'
kind = cl_abap_objectdescr=>exporting
value = REF #( file_path ) )
( name = 'DATA_TAB'
kind = cl_abap_objectdescr=>changing
value = REF #( lt_data ) ) ).
DATA(meth) = 'LIF_READFILE~GUI_UPLOAD'.
*dynamic call
CALL METHOD (classname)=>(meth) PARAMETER-TABLE ptab.
*code under test here..
ENDMETHOD.
ENDCLASS.
我认为您的问题与语言无关。你检查了论坛上提供的多个答案吗?你认为提出的不同方法如何?
您可以使用实例方法将静态调用封装在新接口和类中,该方法映射到静态指定的正确类的静态方法。
ABAP特有的关于使用"类名"作为变量的解决方案,正如您所展示的,我个人不喜欢它,但我认为这只是个人偏好的问题,并不重要。附言:术语"解释语言"是指编程语言,相反(简单地说(是"编译语言";你所说的更多的是一个动态链接。
到目前为止,我已经找到了两个对我来说更好的解决方案;我仍然期待着尝试。
工厂+注入器(依赖项查找(
改进的解决方案1–将实例处理转移到工厂。没有提供工厂和注射器的代码——这是标准解决方案。
CLASS lcl_file_operations DEFINITION.
PUBLIC SECTION.
CLASS-METHODS:
get_file_length
IMPORTING
!file_path TYPE string
RETURNING
VALUE(text_length) TYPE i.
ENDCLASS.
CLASS lcl_file_operations IMPLEMENTATION.
METHOD get_file_length.
myfactory=>get_filereader( )->gui_upload(
EXPORTING file_path = file_path
CHANGING data_tab = lt_data
).
*code under test here..
ENDMETHOD.
ENDCLASS.
Pros
- 正在测试的更干净、更短的代码。实例是在其他地方创建和测试的
Cons
- 它仍然会创建实例
- 总共有更多代码
使用TEST-SAM和TEST-INEJECTION
CLASS zcl_file_operations IMPLEMENTATION.
METHOD get_file_length.
TEST-SEAM seam_gui_upload.
zcl_filereader=>gui_upload(
EXPORTING file_path = file_path
CHANGING data_tab = lt_data
).
END-TEST-SEAM.
*code under test here..
ENDMETHOD.
ENDCLASS.
FOR TESTING方法
*...
TEST-INJECTION seam_gui_upload.
ztc_testdouble=>gui_upload(
EXPORTING file_path = file_path
CHANGING data_tab = lt_data
).
END-TEST-INJECTION.
*...
Pros
- 这似乎是迄今为止最好的解决方案
- 使用类方法;未创建任何实例
- 总共最短的代码
Cons
- 被社区认为是肮脏的技术(仅推荐用于遗留代码(
- 被测代码轻度污染
注意
- 必须在全局类中使用,而不是在本地类中使用