【ABAP】ABAP Unitを使ってみよう(1)　～簡単なところから

##ABAPのテスト自動化
ABAPにもテスト自動化の仕組みが用意されていることをご存じでしょうか。私は[ABAP to the Future](https://www.sap-press.com/abap-to-the-future_4751/)という本で初めて知りました。この本の5章にテスト自動化の仕組み（ABAP Unit）について紹介されています。ただ、テストクラス全体がどういう構成になっているかよくわからなかったので、実際に手を動かしてみることにしました。初めてなので、まずは簡単なところから。徐々に複雑なシナリオにも挑戦していきたいと思います。

目次
【ABAP】ABAP Unitを使ってみよう(1)　～簡単なところから（今回）
[【ABAP】ABAP Unitを使ってみよう(2)　～Test Doubleで依存性を解決する](https://qiita.com/tami/items/117952bfe22ec2ba0e0f)
[【ABAP】ABAP Unitを使ってみよう(3)　～CDSビューのテスト](https://qiita.com/tami/items/4fa6994f0cb4ce336a92)
[【ABAP】ABAP Unitを使ってみよう(4)　～CDS：セッション変数をTest Doubleにしようとして失敗した話](https://qiita.com/tami/items/ba74f66e3c41b05c7f9d)

##ABAP Unitの構成
テストはローカルクラスの中に書きます。テストクラスからは、外部のクラスのメソッドをテストすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/eaa40edd-2f94-b2d1-9b65-018dbf9176db.png)
**【疑問】テスト用クラスのグローバルの部分は使わないのか？**
本番用のコードをここに書くこともできます。その場合、一つのクラスに本番用のコードと、（ローカルクラスとして）テスト用のコードが同居することになります。テストクラスの構成をどうしたらよいのかは、まだよくわかりません。複数のクラスを一か所でテストしたい場合は、テスト用のクラスを独立させたほうがよさそうです。

##ABAP Unitを使ったTest-Driven Develpmentの流れ
Test-Driven Development(TDD)とは、本番のコードより先にテストを書くという開発手法で、以下のサイクルで進めます。
1. テストコードを書く（テストは失敗）
2. 本番用のコードを実装してテストに通るようにする
3. テストコードと本番用コードをリファクタリング（改善）する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/9d51b609-20d3-7ef4-5fd7-0a54b2fcfaf6.png)
[SAP - ABAP Development User Guide](https://help.sap.com/viewer/5371047f1273405bb46725a417f95433/Cloud/en-US/4ec2efe26e391014adc9fffe4e204223.html)より引用

##早速やってみる
###手順
1. 本番用のクラスを作る（ガラだけ）
2. テスト用のクラスを作る
3. テストを実行する
4. 機能を実装して再テストする

###1. 本番用のクラスを作る（ガラだけ）
以下のクラスをテスト対象とします。

```abap
CLASS zcl_mob49_class_under_test DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_mob49_class_under_test IMPLEMENTATION.
ENDCLASS.
```

###2. テスト用のクラスを作る
本番用のクラスとは別に、テスト用のクラスを作成します。テスト用のローカルクラスは"Test Classes"というタブの中に書きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/7092d24c-70fe-f7d7-e4c3-17af7f8ef47c.png)
"test"と打ってからCtrl+Spaceを押すと、テスト用のコードが提案されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c4166756-e724-583b-477a-d420e672828a.png)
以下が初期提案されたコードです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d46fa2e7-6ce2-925a-81d3-e1131888ae55.png)

####テスト用のコード

```abap
*"* use this source file for your ABAP unit test classes
class ltcl_ definition final for testing
  duration short
  risk level harmless.

  private section.
    data:
      mo_class_under_test type ref to zcl_mob49_class_under_test.

    methods:
      setup,
      say_hello_world for testing.
endclass.


class ltcl_ implementation.
  METHOD setup.
    "テスト対象オブジェクトをインスタンス化する
    create OBJECT mo_class_under_test.

  ENDMETHOD.

  method say_hello_world .
    data: lv_message type string.

    mo_class_under_test->hello_world(
      EXPORTING
        i_name    = 'Unit Test'
      IMPORTING
        e_message = lv_message
    ).

    cl_abap_unit_assert=>assert_equals(
      EXPORTING
        act                  = lv_message             " 実際の結果
        exp                  = 'Hello, Unit Test !'   " 想定結果
        msg                  = 'hello world function not working' " エラーの場合のメッセージ
        quit                 = if_aunit_constants=>quit-test
    ).

  endmethod.

endclass.
```
ここでは2つのメソッドを実装しました。

- setup: テスト用のメンバ変数の初期化などを行う。（最初に呼ばれるメソッド）
- say_hello_world: これから実装するメソッドのテスト用。
メソッドからの返り値が想定した値と一致しているかチェックする。

有効化しようとすると、テスト対象のメソッドがまだ実装されていないためエラーになります。

####Quick Fixでメソッドを実装
エラーになったメソッドの上で右クリックし、Quick Fixを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/075da0fc-6442-5bf0-efd1-02997f53f430.png)
"Create method hello world"が提案されるので、それを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/bd32b7d8-dbab-402c-61bf-da97749c8ef5.png)
メソッド名やパラメータが提案されるので、必要に応じて調整し、Finishを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/cd7ce571-038f-9012-4d04-81118703fb1e.png)
テスト対象のクラスにメソッドのガラが実装されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/112bd3b4-e939-177a-ca61-fb2e2e1c7a66.png)

###3. テストを実行する
右クリック>Run As>ABAP Unit Testを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/49ac7fea-9c07-773b-7526-574a516ec581.png)
まだメソッドの中身を実装していないのでエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/eae2c8a6-fd49-d14d-53dc-396e8d887712.png)

###4. 機能を実装して再テストする
hello_worldメソッドを実装します。

```abap
  METHOD hello_world.

    e_message = |Hello, { i_name } !|.

  ENDMETHOD.
```

再テストの結果はOKになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/01626564-7772-4ba0-836f-81ba4b5fd4cc.png)

##テスト自動化、TDDについて思ったこと
以下、思ったことを箇条書きで。

- どこまでテストするのか。すべてのメソッドをテストする必要がある？→汎用モジュールなどの場合、ルーチン単位でテストしているわけではなく、分岐網羅とかでやってる
- 「テストコードの品質はどうやって担保するのか」と言われそう
- 独立したテストができるように、依存関係を解決しなければならないのが厄介


##参考
[Unit Testing with ABAP Unit](https://help.sap.com/viewer/5371047f1273405bb46725a417f95433/Cloud/en-US/08c60b52cb85444ea3069779274b43db.html)