1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ABAP Test Double フレームワークの使用方法

Last updated at Posted at 2021-03-23

ABAP Test Double フレームワークの使用方法

ABAPのTest DoubleのフレームワークであるABAP Unitを用いることで、テスト対象(以下CUT)が依存する先のクラス(以下DOC)のUnit test時のふるまいを変えることができます。ただし、依存性注入パターンに基づいたプロダクションコードを前提としています。

補足

Test DoubleのDoubleは、影武者という意味です。このエントリでは、依存性注入とはコンストラクタ・インジェクションです。つまり、CUTのクラスのコンストラクタに、引数の型としてDOCのオブジェクトのインタフェースをとります。

zcl_greetingsがテスト対象のクラスだとします。一方、 zcl_timlo は、 zcl_greetings から呼び出されます。本来、 zcl_timlo はテストする対象ではありません。しかし、普通にテストしようとすると、 zcl_timlo もついてきてしまいます。これを、「 zcl_greetings は、zcl_timlo に依存する」といいます。

この2つのクラスにインタフェースを定義します。zcl_greetings に対して zif_greetingszcl_timlo に対して zif_timlo を作成します。

上記は、zcl_greetingsのクラスの中に、zcl_timlo というクラス名を書いています。お互いのクラス名を使わず、インタフェース名だけを使うように変えてみます。

下図は、インタフェースを示す (I) と、クラスを示す (C) が交互になります。クラスどうしは線がつながっていません。ABAP のTest Doubleはこの状態を前提としています。

依存性注入

では、zcl_greetings は、zcl_timlo をどうやって使えばいいかというと、本番の場合にはABAPならレポートプログラム(main)、テストの場合にはテストクラスのsetupメソッドか、各テストメソッドに書きます。

main
    timlo = new zcl_timlo(  ).
    cut = new zcl_greetings( timlo ). " コンストラクタの引数にDOCをセット
test
    timlo ?= cl_abap_testdouble=>create( 'zif_timlo' ).
    cut = new zcl_greetings( timlo ). " コンストラクタの引数にDOCをセット

例題

午前に"Good morning!"、午後に"Good afternoon!"と返すプログラムを考えます。期待結果が実行した時刻によって変るのですが、テストが午前中にPassし、午後にFailするのでは困ります。

狙いは、Test doubleによって、zcl_timlo のふるまいを変えることで、午前と午後の状態を作ることです。1

zcl_timlo のメソッド get_local_time は本来sy-timeloを返します。これを使う代わりに、フレームワークで作ったインスタンスに代役を務めさせ、固定値を返すようにします。

プロダクションコード:インタフェース

インタフェースを定義します。

zif_timlo
INTERFACE zif_timlo
  PUBLIC.
  METHODS get_local_time
    RETURNING VALUE(r_result) TYPE t.
ENDINTERFACE.

クラスの定義と実装

ローカル時刻を返すクラスを実装します。

zcl_timlo
CLASS zcl_timlo DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_timlo.
ENDCLASS.


CLASS zcl_timlo IMPLEMENTATION.
  METHOD zif_timlo~get_local_time.
    r_result = sy-timlo.
  ENDMETHOD.
ENDCLASS.

時刻が午前の時には文字列Good morning、午後には文字列Good afternoonを返すクラスおよびメソッドを実装します。引数に zif_timlo の参照をとるコンストラクタを用いています。コンストラクタ・インジェクションによる依存性注入です。

zcl_greetings
CLASS zcl_greetings DEFINITION
  PUBLIC FINAL
  CREATE PUBLIC.

  PUBLIC SECTION.
    INTERFACES zif_greetings.

    METHODS constructor
      IMPORTING io_timlo TYPE REF TO zif_timlo.

  PRIVATE SECTION.
    CONSTANTS noon TYPE t VALUE '120000'.
    DATA timlo TYPE REF TO zif_timlo.

ENDCLASS.


CLASS zcl_greetings IMPLEMENTATION.
  METHOD zif_greetings~say_greetings.
    IF timlo->get_local_time( ) < noon.
      r_result = `Good morning!`.
    ELSE.
      r_result = `Good afternoon!`.
    ENDIF.
  ENDMETHOD.

  METHOD constructor.
    timlo = io_timlo.
  ENDMETHOD.
ENDCLASS.

なお、zcl_greetingsについても、インタフェースが定義されています。

zif_greetings
INTERFACE zif_greetings
  PUBLIC.
  METHODS say_greetings
    RETURNING VALUE(r_result) TYPE string.
ENDINTERFACE.

テスト

テストは、zcl_greetings のローカルクラスlcl_greetingsとして定義します。2種類のテストメソッドがあり、それぞれ午前のケース、午後のケースを表します。

lcl_greetings
CLASS lcl_greetings DEFINITION FINAL
  FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.

  PRIVATE SECTION.
    METHODS _01_say_morning_greetings   FOR TESTING.
    METHODS _02_say_afternoon_greetings FOR TESTING.

ENDCLASS.

各テストメソッド で、cl_abap_testdouble=>create() を呼び出します。これは、DOCであるzcl_timlo のかわりに、同じインタフェースのインスタンスが生成をおこないます。ただ、生成されるオブジェクトはObject型なので、?=をつかってキャストをします。=だと、静的にzif_timlo型に割り当てられずエラーとなります。?=を使うと、有効化時のチェックを回避できます。

lcl_greetings->_01_say_morning_greetings
    DATA double_timlo TYPE REF TO zif_timlo.
    double_timlo ?= cl_abap_testdouble=>create( 'zif_timlo' ).

cl_abap_testdouble=>configure_call( double_timlo ) でreturn値を設定します。どのメソッドが対象かについて、一度メソッドをコールすることでテストフレームワークに知らせます。

lcl_greetings
  METHOD _01_say_morning_greetings.
    DATA double_timlo TYPE REF TO zif_timlo.
    DATA cut          TYPE REF TO zif_greetings.

    double_timlo ?= cl_abap_testdouble=>create( 'zif_timlo' ).
    cut = NEW zcl_greetings( double_timlo ).
    cl_abap_testdouble=>configure_call( double_timlo )->returning( '11:00:00' ).
    double_timlo->get_local_time( ). " 一度空呼び

    cl_abap_unit_assert=>assert_equals( exp = 'Good morning!' "期待値
                                        act = cut->say_greetings( ) "テストの結果
                                        msg = 'Assert: 01 greeting should be good morning' ).
  ENDMETHOD.

あらためてテスト全体

lcl_greetings
CLASS lcl_greetings DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.

PRIVATE SECTION.
  METHODS _01_say_morning_greetings   FOR TESTING.
  METHODS _02_say_afternoon_greetings FOR TESTING.

ENDCLASS.

CLASS lcl_greetings IMPLEMENTATION.
METHOD _01_say_morning_greetings.
  DATA double_timlo TYPE REF TO zif_timlo.
  DATA cut          TYPE REF TO zif_greetings.

  double_timlo ?= cl_abap_testdouble=>create( 'zif_timlo' ).
  cut = NEW zcl_greetings( double_timlo ).
  cl_abap_testdouble=>configure_call( double_timlo )->returning( '11:00:00' ).
  double_timlo->get_local_time( ).

  cl_abap_unit_assert=>assert_equals( exp = 'Good morning!'
                                      act = cut->say_greetings( )
                                      msg = 'Assert: 01 greeting should be good morning' ).
ENDMETHOD.

METHOD _02_say_afternoon_greetings.
  DATA double_timlo TYPE REF TO zif_timlo.
  DATA cut          TYPE REF TO zif_greetings.

  double_timlo ?= cl_abap_testdouble=>create( 'zif_timlo' ).
  cut = NEW zcl_greetings(  double_timlo ).
  cl_abap_testdouble=>configure_call( double_timlo )->returning( '13:00:00' ).
  double_timlo->get_local_time( ).
  cl_abap_unit_assert=>assert_equals( exp = 'Good afternoon!'
                                      act = cut->say_greetings( )
                                      msg = 'Assert: 02 greeting should be good afternoon' ).
ENDMETHOD.
ENDCLASS.

まとめ

cl_abap_testdoubleを用いるためには、DOCのインタフェースを使ってソースコード上の依存性を排除したうえで、テストクラスからテストダブルのインスタンスを渡せるような設計にしておく必要があります。このためには、テスト容易性のために、本番コードを変える必要があります。ただ、その前提であれば、ABAP UnitのTest Doubleは、簡易に利用することができる方法だと思います。ローカルクラスなどにテスト用の依存先を実装する方法と組み合わせる方法も理解したうえで、適切に使い分けることが必要だと思います。

  1. ABAPの標準でついてくるフレームワークなら、sy-timlo を書き換える仕組みが欲しいところですが、そのようなことはできません。テスト対象クラスのローカルクラスでテストする場合に限定すればTest Seamを使うという手もありますが、ここでは取り扱いません(注:ご指摘ありがとうございます)。

1
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?