はじめに
この記事は、「ABAP Unitを使ってみよう」シリーズの2回目です。
1回目の記事はこちら↓
【ABAP】ABAP Unitを使ってみよう(1) ~簡単なところから
自動テストにおける依存性の問題
自動テストでは、テスト対象のソースコードが変わらない限り、実行の都度同じ結果が得られることが求められます。このためには実行環境(たとえば、DBに入っているデータ)によってテストが影響を受けないようにする必要があります。
テスト対象のコードが依存する他のコードやDBなどをdepended-on component(DOC)と呼び、テストではDOCが毎回同じ動きをするようにコントロールする必要があります。

Managing Dependencies with ABAP Unitより引用
Test Double
Test Doubleとは、テストのときだけDOCに代わって動くコードです。本番用のコードだとDOCが返す結果が変わってしまう場合に、Test Doubleを用意します。あくまでもテスト対象のコードは本番と同じコードであり、テスト対象の外部にあるコードを置き換える、というところがポイントです。

Managing Dependencies with ABAP Unitより引用
Test Doubleの種類
Test Doubleを作成するためにTest Double Frameworkというものが用意されており、以下の種類があります。
| DOCの種類 | Test Double | 
|---|---|
| クラス | ①Test Doubleクラスをマニュアルで作る | 
| ②ABAP Test Double Framework | |
| データベース(CDS) | CDS Test Double Framework | 
| データベース(SQL) | ABAP SQL Test Double Framework | 
| その他(ソースコード) | ABAP Test Seams | 
【今回のテーマ】ABAP SQL Test Double Framework
今回の記事では、ABAP SQL Test Double Frameworkを使ってDBへのアクセスをTest Doubleに置き換えてみます。
概要
IF_OSQL_TEST_ENVIRONMENTというインターフェースがTest Doubleを作成します。テストの要領は以下の通りです。
- テストクラスの初期処理で、使用するテーブルを宣言してTest Doubleを生成する
- 各テストの前にテストデータをクリアする(毎回同じ結果になるように)
- テストデータを挿入する(本物のDBではなく、Test Doubleに)
- テストが終わったらTest Doubleも終了する
テストシナリオ
キーを指定してテーブルZPRICEを読み込むメソッドを作成します。テストでは、データが正しく取得できていることを確認します。
テストコード
テストは前回の記事と同じテストクラスに書いています。
class_setup
このメソッドはテストクラス実行時、最初に1回だけ呼ばれます。
  METHOD class_setup.
    "使用するテーブルを指定してTest Doubleを生成
    environment = cl_osql_test_environment=>create(
      i_dependency_list = VALUE #( ( 'ZPRICE' ) )
    ).
  ENDMETHOD.
setup
このメソッドは、各テストメソッドが実行される前に呼ばれます。
  METHOD setup.
    ...
    "各テスト実行前にTest Doubleをリフレッシュする
    environment->clear_doubles( ).
  ENDMETHOD.
class_teardown
このメソッドは、テストクラスの終了前に呼ばれます。
  METHOD class_teardown.
    "Test Doubleを終了する
    environment->destroy( ).
  ENDMETHOD.
get_price
テーブルZPRICEを読み込むメソッドのテストです。
  METHOD get_price.
    data: lt_price type standard table of zprice,
          ls_price type zprice,
          ls_price_exp type zprice,
          lv_material type matnr.
    " テストデータを挿入
    ls_price_exp = value #( MATERIAL = 'MAT-001' TEXT = 'Test' PRICE = '100.00' CURRENCY = 'USD' ).
    append ls_price_exp to lt_price.
    environment->insert_test_data( lt_price ).
    " テスト対象のメソッドを実行
    lv_material = 'MAT-001'.
    ls_price = mo_class_under_test->get_price( iv_material = lv_material ).
    cl_abap_unit_assert=>assert_equals(
      EXPORTING
        act                  = ls_price             " 実際の結果
        exp                  = ls_price_exp         " 想定結果
        msg                  = 'get price function not working' " エラーの場合のメッセージ
        quit                 = if_aunit_constants=>quit-test
    ).
  ENDMETHOD.
実行結果
最初はテスト対象のメソッドが空の状態で実施します。結果は以下の通りエラーになります。ここで気づいたのですが、テストの実施順はメソッドの定義順ではなく、メソッドのアルファベット順になるようです。

テスト対象のメソッド実装
  METHOD get_price.
    SELECT SINGLE material, text, price, currency
    FROM zprice
    WHERE material = @iv_material
    INTO CORRESPONDING FIELDS OF @r_result.
  ENDMETHOD.
テスト結果
正常終了になりました。(say_helloのテストを最初に実行するように、メソッド名の先頭にprifixを付けました)

感想
DBがからむテストは、通常はテストデータを手で登録したり、トランザクションを使って登録したりと結構手間だったりします(テストパターンが多いとさらに大変)。Test Doubleを使うと欲しいデータをすぐに用意できるメリットがあると感じました。

