如何在单元测试中模拟权限检查?

2020-08-25 18:37发布

         点击此处--->   EasySAP.com群内免费提供SAP练习系统(在群公告中)

加入QQ群:457200227(SAP S4 HANA技术交流) 群内免费提供SAP练习系统(在群公告中)


我最近的博客文章,我第一次涉足ABAP单元测试,这要归功于 Jacques Nomssi Nzali Mike Pokraka 和其他几个 其他人,我已经学到了很多东西,并且一切都很好。

我现在确实有一个未解决的问题,而不是将其隐藏在我的博客文章的评论中,我决定为其启动一个新的问答线程。 目前,我使用Z表来查看是否允许特定用户执行某些操作,并且此操作应按预期进行,并且我还通过模拟表访问获得了合适的测试用例。 因为我仍处于沙盒系统的原型模式下,所以这是实现它的最简单方法,并且比我要替换的代码中的硬编码检查要好。

在将代码移至实际开发系统时,我想将此逻辑切换为适当的授权对象,并检查是否允许用户执行操作。 因此,我的问题是:然后如何在单元测试期间"模拟"权限检查?

我已经在寻找答案,但是即使在此过程中我发现了其他有趣的文章(即 ABAP单位最佳做法 with-abap-unit-tests/" target =" _ blank">使用ABAP单元测试),没有一个包含我特定问题的答案。 那么,有没有一种可以在NW 7.50系统中工作并且不太需要实施呢? 例如,我可以想象按照另一种接口定义(一种用于auth而不是表选择)进行操作,但是不确定这是否是最佳选择。

非常感谢和欢呼

Baerbel

         点击此处--->   EasySAP.com群内免费提供SAP练习系统(在群公告中)

加入QQ群:457200227(SAP S4 HANA技术交流) 群内免费提供SAP练习系统(在群公告中)


我最近的博客文章,我第一次涉足ABAP单元测试,这要归功于 Jacques Nomssi Nzali Mike Pokraka 和其他几个 其他人,我已经学到了很多东西,并且一切都很好。

我现在确实有一个未解决的问题,而不是将其隐藏在我的博客文章的评论中,我决定为其启动一个新的问答线程。 目前,我使用Z表来查看是否允许特定用户执行某些操作,并且此操作应按预期进行,并且我还通过模拟表访问获得了合适的测试用例。 因为我仍处于沙盒系统的原型模式下,所以这是实现它的最简单方法,并且比我要替换的代码中的硬编码检查要好。

在将代码移至实际开发系统时,我想将此逻辑切换为适当的授权对象,并检查是否允许用户执行操作。 因此,我的问题是:然后如何在单元测试期间"模拟"权限检查?

我已经在寻找答案,但是即使在此过程中我发现了其他有趣的文章(即 ABAP单位最佳做法 with-abap-unit-tests/" target =" _ blank">使用ABAP单元测试),没有一个包含我特定问题的答案。 那么,有没有一种可以在NW 7.50系统中工作并且不太需要实施呢? 例如,我可以想象按照另一种接口定义(一种用于auth而不是表选择)进行操作,但是不确定这是否是最佳选择。

非常感谢和欢呼

Baerbel

付费偷看设置
发送
7条回答
Nan4612
1楼 · 2020-08-25 19:04.采纳回答

好的,让我们从一个简单的示例开始。

首先,我需要一个接口

接口if_authorization_checker。
   方法check_tcode
     输入
       iv_transaction TYPE密码
     正在返回
       VALUE(rv_result)类型abap_bool。
 结束界面。
 

我有真实的类来测试授权

 CLASS lc_authorization_checker DEFINITION。
   公共部分。
     接口if_authorization_checker。
 ENDCLASS。

 类别lc_authorization_checker IMPLEMENTATION。
   方法if_authorization_checker〜check_tcode。
     权限检查对象'S_TCODE'ID'TCD'字段iv_transaction。
     rv_result = SWITCH#(sy-subrc,当0那时abap_true
                                 否则abap_false)。
   终结法。
 ENDCLASS。

 

但是我也有一个用于相同接口的Mock

 CLASS lc_authorization_checker_mock DEFINITION。
   公共部分。
     接口if_authorization_checker。
     方法set_tcode_result
       输入
         iv_result TYPE abap_bool。
     数据result_tcode TYPE abap_bool。
 ENDCLASS。
 
 CLASS lc_authorization_checker_mock IMPLEMENTATION。
   方法if_authorization_checker〜check_tcode。
     rv_result = result_tcode。
   终结法。


   方法set_tcode_result。
     result_tcode = iv_result。
   终结法。
 ENDCLASS。
 

模拟方法还包含全局变量RESULT_TCODE。 这个变量只包含我应该收到的结果。

mock方法还需要一种方法来填充此变量。

您现在需要将该类注入最终的类中。

 CLASS lc_my_beautiful_class DEFINITION。
   公共部分。
     方法构造函数
       输入
         io_authorization_checker类型参考if_authorization_checker可选。
     方法my_beautiful_method
       正在返回
         VALUE(rv_result)类型abap_bool。
     数据o_authorization_checker类型参考if_authorization_checker。
 ENDCLASS。

 
 CLASS lc_my_beautiful_class IMPLEMENTATION。
   METHOD构造函数。
     o_authorization_checker = COND#(当io_authorization_checker被绑定然后io_authorization_checker
                                       其他的lc_authorization_checker()。
   终结法。
   方法my_beautiful_method。
     rv_result = o_authorization_checker-> check_tcode(sy-tcode)。
   终结法。
 ENDCLASS。

 

因此,我在最后一堂课的构造函数中有一个可选属性。 此构造函数确定参数是否未绑定来创建真实的参数。

(在纯净代码中,您将需要一个Factory,但会使代码有些复杂)

现在在您的测试类中,您需要创建一个 MOCK,然后使用构造函数进行注入。

所以这里有点复杂

 CLASS ltc_my_beautiful_class定义
       用于检测
       危险等级危害
       持续时间短
       最后。
   公共部分。
     方法test_my_beautiful_method用于测试。
 ENDCLASS。


 类别ltc_my_beautiful_class IMPLEMENTATION。
   方法test_my_beautiful_method。
     "鉴于我有一个经过测试的课程的实例。
     数据o_authorization类型参考if_authorization_checker。
     数据o_authorization_mock类型参考lc_authorization_checker_mock。
     o_authorization_mock = NEW#()。
     o_authorization?= o_authorization_mock。


     DATA(o_cut)=新lc_my_beautiful_class(o_authorization)。
     "鉴于结果应该是积极的
     o_authorization_mock-> set_tcode_result(abap_true)。
     "当我测试我的方法时
     DATA(rv_authorization_result)= o_cut-> my_beautiful_method()。
     "那么什么也没有追加
     cl_abap_unit_assert => assert_true(
       act = rv_authorization_result)。
   终结法。
 ENDCLASS。
 

O_Authorization包含一个与我最终类的构造函数所期望的接口相对应的类型。

O_Authorization_Mock包含实际的模拟类,并有可能推送预期的结果

jovirus
2楼-- · 2020-08-25 19:07

Sandra Rossi

嗨,Sandra-很抱歉,但是您的回应 对我而言,太晦涩难懂了,因为到目前为止,我还只是涉足ABAP OO和单元测试的表面。

南山jay
3楼-- · 2020-08-25 18:53

您描述了单元测试的全部内容。 用更通用的术语来说:组件A(您的应用程序)取决于组件B(权限检查)。 如果依赖组件(DOC)尚不可用,则可以对其进行模拟或存根,或开发临时解决方案(Z-Table)。 并行或增量开发的好方法!

如果单元测试对DOC进行了抽象,则理想情况下应该能够在不更改任何测试或高级产品代码的情况下切换DOC。 这就是使良好的单元测试成为安全网的原因。

您正面临一个问题,这可能表明您的测试水平可能太低,这是普遍的趋势,并且也是其中之一。 使单元测试变得比原来更麻烦的事情。 因此,通常应避免测试私有方法,这是一个很好的理由。

因此,在您的情况下,我会在一个方法或类中添加auth检查(如Sandra的答案所示),并且您的单元测试会对此进行模拟 。 产品代码最初会查找Z表,但随后会执行身份验证检查。 只要方法/类的接口保持不变,并且您的单元测试模拟该接口,那么在更改基础功能时就不需要对测试进行任何更改。 在这里,我们看到了单元测试的真正好处!

compass1988
4楼-- · 2020-08-25 18:48

在上面的示例中,CHECK将在生产代码中执行AUTHORITY-CHECK,或者在测试中执行两次测试。

您可以模拟任何东西:

包装要模拟的方法中的代码,最终根据您的情况将方法放在专用类中(ZCL_AUTH_CHECKER)

创建测试 double(类),它将"重新定义" CHECK方法以返回您首次注入的结果。 在您的情况下,您可以注入一个内部假授权(对象,字段,值)表,然后重新定义的CHECK会读取内部表。

在测试类中,您将测试双精度注入ZCL_AUTH_CHECKER, 您注入了伪造的授权,然后调用了受测代码。

hengyuye
5楼-- · 2020-08-25 19:08

更新:

感谢 Sandra Rossi Mike Pokraka 弗雷德里克·吉罗德(Frederic Girod)我继续并为该类实现了附加的类和接口 auth检查,但是按照我已经必须要做的 表选择可测试的(仅来自本地测试类的代码段):

 [...]
 CLASS ltd_auth测试定义。
   "本地测试类以模拟权限检查例程
   公共部分。
   接口:zif_check_wb_action_auth。
   数据:
   zbc_ddic_check带默认密钥的zbc_ddic_check类型标准表。
 ENDCLASS。

 CLASS ltd_auth实施。
   方法zif_check_wb_action_auth〜user_has_ddic_auth。
   "对于模拟身份验证检查,我们在ZBC_DDIC_CHECK_ENTRY的内部表示中使用特殊条目
   "这模拟了一个用户,该用户的身份验证检查失败,但无论如何仍可以通过Z表中的条目进行DDIC更改
   "(但很可能是暂时的)。
   数据:lv_char40类型char40。

   清除r_result。
   lv_char40 = i_uname。

   读取表zbc_ddic_check和键check_title ='DDIC-USER-NO-AUTH'
   Checked_entity = lv_char40
   check_active = abap_true
   INTO DATA(zbc_ddic_check_entry)。

   IF zbc_ddic_check_entry-check_active EQ abap_true。
   r_result = abap_true。
   其他。
   r_result = abap_false。
   万一。
   终结法。
 ENDCLASS。
 [...]

 类别ltc_action定义,用于测试持续时间短的风险等级危害。
   "本地测试类来测试检查逻辑
   专用部分。
   数据:
   将TYPE REF剪切为zcl_check_wb_action,
   dao TYPE REF TO ltd_青岛
   身份验证类型引用为ltd_auth。  " <-添加
 [...]
 ENDCLASS。

 类别ltc_action IMPLEMENTATION。
   方法设置。
   dao = NEW#()。
   验证=新#()。  " <-添加
   cut = NEW#()。
   cut-> _ dao = dao。  "注入表选择
   cut-> _ auth = auth。"注入auth-check" <---已添加
 [...]
   "使用示例数据填充ZBC_DDIC_CHECK的模拟内部表,以测试身份验证检查

   auth-> zbc_ddic_check = VALUE#((check_title ='DDIC-USER-NO-AUTH'
   check_entity ='DDICNOAUTH'check_active = abap_true)
   )。

测试方法本身保持不变。 我确实有一些错误 同时添加身份验证检查逻辑并定期重新运行测试。 最初,我错过了必须(后见之明!)定义和 在测试类中实现auth-check接口,从而导致 运行测试时的技术问题。 弄清楚之后, 我确实没有通过其中一项测试的实际失败,直到我意识到自己 还必须为Z表的内部模型提供测试数据 验证检查逻辑(auth-> zbc_ddic_check)。

感谢您的帮助!

ZJXianG
7楼-- · 2020-08-25 18:51

谢谢,迈克!

您会很高兴地读到,即使我也从针对私有方法的单元测试开始,但最终我终于摆脱了所有这些方法,现在仅对带有测试的公共方法进行了单元测试。 覆盖率100%。

欢呼声

贝尔贝尔

一周热门 更多>