mcpField
G
generate-abap-unit-test

generate-abap-unit-test

github.com/arc-mcpテスト・品質239264 回インストール2 日前に更新
リポジトリ

generate-abap-unit-test について

generate-abap-unit-testは、ABAP開発者向けのクラス単位のテスト生成スキルです。依存関係を分析してテストケースを作り、インターフェースベースのテストダブルを使ったABAP Unitコードを生成し、プレビュー、書き込み、アクティベーション、実行まで支援します。

主な機能

  1. 01クラスのソース、メソッド、依存関係を取得
  2. 02分岐、エラー、出力に基づくケース設計
  3. 03インターフェースベースのテストダブル
  4. 04ABAP Unitテストクラスの生成
  5. 05コード書き込み、アクティベーション、実行

活用シーン

  1. 01ABAPクラスの公開メソッド向けテスト生成
  2. 02メソッドの分岐とエラー経路のカバー
  3. 03注入された依存関係を分離したクラスロジックのテスト
  4. 04テスト実行と失敗原因の調査

MCP 対応

ARC-1でSAPクラスの読み取りと更新、コードのアクティベーション、単体テストの実行を行います。また、mcp-sap-docsでABAP Unitとテストダブルのパターンを調べます。MCPクライアント向けのサービス提供については記載されていません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/arc-mcp/arc-1 --skill generate-abap-unit-test

インストール後、Claude Code で/generate-abap-unit-testを入力して読み込まれたか確認します。

詳細

ABAP Unit テストの生成

依存関係の分析、テストダブル、メソッド単位の手術的な変更を用いて、クラスの ABAP Unit テストを生成します。

このスキルは、ARC-1(SAP システムへのアクセス)と mcp-sap-docs(ドキュメントとベストプラクティス)を組み合わせ、ABAP クラス向けに SAP Joule の「Unit Test Generation」機能を再現します。CDS エンティティを対象とする CDS 単体テストスキルとは異なり、このスキルは ABAP クラスを対象とし、CDS Test Double Framework の代わりにインターフェースベースのテストダブルを使用します。

スマートデフォルト(暗黙に適用し、質問しない)

設定デフォルト理由
テストクラス名ltc_<short_class_name>グローバルクラスの ABAP Unit テストは、ローカルの testclasses インクルード(CCAU)に配置します
テスト対象メソッドすべてのパブリックメソッド一覧を確認した後、ユーザーが対象を絞り込めます
パッケージ$TMP迅速なプロトタイピングに適しています
リスクレベルHARMLESSテストダブルは実データを変更しません
実行時間SHORT単体テストは高速であるべきです
モック戦略インターフェースベースのテストダブル依存性注入に最適なパターンです

入力

ユーザーはテスト対象の ABAP クラス(例:ZCL_TRAVEL_HANDLER)を指定します。

必須なのはクラス名のみです。ユーザーがクラス名だけを指定した場合は、スマートデフォルトを適用してすぐに進めます。

ユーザーは任意で次の項目を指定できます。

  • テストクラス名(デフォルト:対象クラスの testclasses インクルード内のローカルクラス ltc_<short_class_name>)
  • テスト対象メソッド(デフォルト:すべてのパブリックメソッド)
  • パッケージ(デフォルト:$TMP)
  • 移送依頼($TMP 以外のパッケージでのみ必要)

ステップ 1:クラスのコンテキストを収集する

クラス、そのメソッド、依存関係、既存のテストを読み取ります。

1a. クラスのソース全体を読み取る

メタデータとすべてのインクルード(既存のテストを含む)を 1 回の呼び出しで取得できるため、構造化形式を優先してください。

SAPRead(type="CLAS", name="<class_name>", format="structured")

説明、カテゴリ、パッケージなどのメタデータと、分割されたソース(main、testclasses、definitions、implementations、macros)を含む JSON が返されます。testclasses フィールドには既存のテストコードが含まれます(存在する場合)。別途取得せずに、現在のテスト範囲を分析するのに役立ちます。

メインソースのみを取得する場合は、次を使用します。

SAPRead(type="CLAS", name="<class_name>")
1b. シグネチャを含むすべてのメソッドを一覧表示する
SAPRead(type="CLAS", name="<class_name>", method="*")

シグネチャと可視性(public/protected/private)を含むすべてのメソッドが返されます。これを使ってクラスの API を把握し、テスト対象のメソッドを特定します。

1c. 依存関係のコンテキストを取得する
SAPContext(type="CLAS", name="<class_name>")

すべての依存関係を自動的に抽出し、それぞれの圧縮されたパブリック API コントラクトを取得します。これにより、次の情報が得られます。

  • 実装されたインターフェースとそのメソッド
  • スーパークラスの階層
  • 注入された依存関係(コンストラクターのパラメーター)
  • 使用されているクラスとそのパブリック API
  • アクセスされるデータベーステーブル

依存関係の連鎖が深い複雑なクラスでは、depth=2 を使用します。

SAPContext(type="CLAS", name="<class_name>", depth=2)
1d. (任意)既存のテストクラスを読み取る

ステップ 1a で format="structured" を使用した場合、testclasses フィールドに既存のテストコードがすでに含まれています(存在しない場合は null)。その場合、このステップは省略してください。

それ以外の場合は、テストクラスを個別に取得します。

SAPRead(type="CLAS", name="<class_name>", include="testclasses")

テストクラスが存在しない場合、取得に失敗することがありますが、問題ありません。テストが存在する場合は、カバレッジの重複を避け、既存のテストパターンに従えるように内容を分析してください。

ステップ 2:メソッドを分析し、テストケースを提案する

各パブリックメソッドについて、ソースコードを分析し、テスト可能なシナリオを特定します。

依存関係の分類

クラスが使用する依存関係を分類します。

カテゴリ確認する対象テスト戦略
モック可能インターフェース(if_*、zif_*)を介したコンストラクター注入そのインターフェースを実装するテストダブルを作成します
スタブ可能データベースアクセス(SELECT、INSERT、UPDATE、DELETE)SETUP でフィクスチャデータを準備するか、DB レイヤーにテストダブルを使用します
透過的内部の private/protected ヘルパーメソッドモックしません。通常どおり実行します
フレームワークSAP フレームワーククラスへの静的呼び出し(例:cl_abap_context_info)注入可能なインターフェースでラップするか、制約を受け入れます
メソッドごとの分析

各パブリックメソッドについて、以下を特定します。

  • 分岐:IF/CASE/COND/SWITCH 文 — 各分岐にテストが必要です
  • エラーパス:RAISE EXCEPTION、TRY-CATCH ブロック — エラーシナリオをテストします
  • 戻り値:メソッドが生成するもの — 出力を検証します
  • 状態の変更:インスタンス属性またはデータベースへの副作用 — 変更を検証します
  • 使用される依存関係:呼び出されるモック可能な依存関係 — 期待値を設定します
ユーザーへの出力

特定したテストケースを、メソッドごとにグループ化した番号付きリストで提示します。

Identified test cases for ZCL_TRAVEL_HANDLER:

Method: CREATE_TRAVEL (public)
  1. [HAPPY] test_create_travel_success — Create travel with valid data, verify travel ID returned
  2. [ERROR] test_create_no_customer — Missing customer, expect CX_TRAVEL_ERROR
  3. [BRANCH] test_create_with_discount — Discount > 0 triggers calculation path

Method: VALIDATE_DATES (public)
  4. [HAPPY] test_dates_valid — End date after begin date
  5. [ERROR] test_dates_reversed — Begin date after end date, expect exception
  6. [EDGE] test_dates_same_day — Same begin and end date, should be valid

ユーザーに次のように尋ねます。「どのテストケースを生成しますか?(すべて/特定の番号/スキップするものはありますか?)」

ユーザーが「すべて」と答えた場合、または希望を示さずに応答した場合は、すべて生成します。

ステップ 3:テストフレームワークのリファレンスを取得する

mcp-sap-docs を使って、最新の ABAP Unit とテストダブルのパターンを取得します。

search("ABAP Unit cl_abap_unit_assert")
search("test double interface mock ABAP")

取得したドキュメントを、生成するコードパターンの参考にしてください。次の点を確認してください。

  • cl_abap_unit_assert の正しいアサーションメソッド名(assert_equals、assert_initial、assert_not_initial、assert_bound、fail)
  • インターフェースのテストダブルを作成するパターン
  • FOR TESTING クラスとメソッドの構文
  • リスクレベルと実行時間のアノテーション

ステップ 4:テストクラスを生成する

次の構造に従って、完全な ABAP テストクラスを生成します。

テンプレート
"! @testing <CLASS_UNDER_TEST>
CLASS <test_class_name> DEFINITION
  FINAL
  FOR TESTING
  DURATION SHORT
  RISK LEVEL HARMLESS.

  PUBLIC SECTION.
  PROTECTED SECTION.
  PRIVATE SECTION.
    CLASS-DATA cut TYPE REF TO <class_under_test>.
    " Test doubles for mockable dependencies
    CLASS-DATA mock_<dep1> TYPE REF TO <interface_1>.
    CLASS-DATA mock_<dep2> TYPE REF TO <interface_2>.

    CLASS-METHODS class_setup RAISING cx_static_check.
    CLASS-METHODS class_teardown.
    METHODS setup.

    "! <description of test case 1>
    METHODS <test_method_1> FOR TESTING RAISING cx_static_check.
    "! <description of test case 2>
    METHODS <test_method_2> FOR TESTING RAISING cx_static_check.
    " ... one method per selected test case
ENDCLASS.

CLASS <test_class_name> IMPLEMENTATION.

  METHOD class_setup.
    " One-time initialization — create test doubles
    " If using a test double framework:
    " mock_<dep1> = cl_abap_testdouble=>create( '<interface_1>' ).
    " Or create a local test double class (see below)
  ENDMETHOD.

  METHOD class_teardown.
    " Clean up
  ENDMETHOD.

  METHOD setup.
    " Per-test CUT instantiation with fresh state
    " Inject test doubles via constructor
    cut = NEW <class_under_test>(
      io_<dep1> = mock_<dep1>
      io_<dep2> = mock_<dep2>
    ).
  ENDMETHOD.

  METHOD <test_method_1>.
    " Arrange — configure mock behavior
    " cl_abap_testdouble=>configure_call( mock_<dep1>
    "   )->returning( VALUE #( <expected_return> ) )->and_expect( )->is_called_once( ).
    " mock_<dep1>-><method>( ).

    " Act — call the method under test
    DATA(lv_result) = cut-><method>( <params> ).

    " Assert — verify expected behavior
    cl_abap_unit_assert=>assert_equals(
      act = lv_result
      exp = <expected_value>
      msg = '<assertion message>' ).
  ENDMETHOD.

  METHOD <test_method_error>.
    " Arrange — set up conditions that trigger the error

    " Act & Assert — expect exception
    TRY.
        cut-><method>( <invalid_params> ).
        cl_abap_unit_assert=>fail( msg = 'Expected exception was not raised' ).
      CATCH <exception_class> INTO DATA(lx_error).
        " Optionally verify exception details
        cl_abap_unit_assert=>assert_not_initial(
          act = lx_error->get_text( )
          msg = 'Exception message should not be empty' ).
    ENDTRY.
  ENDMETHOD.

  " ... more test methods

ENDCLASS.
ローカルテストダブルのパターン

cl_abap_testdouble を利用できない場合、またはインターフェースが複雑な場合は、ローカルテストダブルクラスを作成します。

" Local test double — defined in the test include
CLASS lcl_mock_<dep> DEFINITION FOR TESTING.
  PUBLIC SECTION.
    INTERFACES <interface>.
    DATA last_<method>_input TYPE <param_type>.
    DATA <method>_result TYPE <return_type>.
ENDCLASS.

CLASS lcl_mock_<dep> IMPLEMENTATION.
  METHOD <interface>~<method>.
    last_<method>_input = <param>.
    <return_param> = <method>_result.
  ENDMETHOD.
ENDCLASS.
テストデータの生成ルール

テストデータを生成する際は、次の原則に従ってください。

  1. 最小限のデータ:シナリオの実行に必要な最小限の値を使用します。通常、テストごとに 1~3 件のレコードを使用します。
  2. わかりやすい値:期待される結果を簡単に検証できる値を選びます。
    • 計算の場合:37.83 - 4.27 ではなく、100.00 - 10.00 = 90.00 を使用します
    • ステータスの確認の場合:IF/CASE 式で使用されているステータス値をそのまま使用します
    • 文字列操作の場合:短く、見分けやすい文字列を使用します
  3. 型の一致:ABAP の型に正確に合わせます。
    • NUMC フィールドには '001'(先頭にゼロを付けた文字列)を使用します
    • DEC/CURR フィールドには '100.00' を使用します
    • DATS フィールドには '20260101' を使用します
    • ABAP_BOOL には abap_true / abap_false を使用します
  4. 決定性:sy-datum、sy-uzeit、cl_abap_context_info=>get_system_date( )、乱数値は使用しないでください
  5. 独立性:各テストメソッドを自己完結させます。セットアップメソッドで CUT の状態を毎回初期化します
命名規則
  • テストクラス:クラスの testclasses インクルード内にあるローカルクラス ltc_<short_class_name>
  • テストメソッド:test_<method>_<scenario>(例:test_create_success、test_validate_no_date)
  • メソッド名には snake_case を使用します(テストメソッドの ABAP 規約)
  • メソッド名は 30 文字未満にします
  • モック変数:mock_<short_dep_name>(例:mock_persistence、mock_validator)

ステップ 5:プレビューして確認する

生成した testclasses インクルードのソースコード全体をユーザーに表示し、次のように尋ねます。

「生成した testclasses インクルードはこちらです。SAP システムに書き込みますか?(はい/先に編集/キャンセル)」

ユーザーが編集を希望する場合は、先に反映してから次に進みます。

ステップ 6:書き込み、有効化、テスト

6-pre. 生成コードの lint チェック(任意)

書き込む前に、lint ルールに照らして生成コードを検証します。

SAPLint(action="lint", source="<generated_testclasses_include>", name="<class_under_test>")

続行する前に、lint で見つかった問題を修正します。有効な場合(デフォルト:オン)、書き込み前の lint 検証も自動的に実行されます。

6a. ローカルテストクラスを対象クラスの testclasses インクルードに書き込む
SAPWrite(action="update", type="CLAS", name="<class_under_test>", include="testclasses", source="<generated_testclasses_include>", transport="<transport>")

これはローカルインクルードへの書き込みであり、独立したグローバルクラスの作成ではありません。ロックハンドルを渡したり、ABAP ソースにロック/ロック解除のコードを追加したりしないでください。ARC-1 が ADT のロック → CCAU インクルードが存在しない場合は初期化 → 書き込み → ロック解除の手順を内部で実行します。新規クラスの場合、ARC-1 は書き込み前に不足している testclasses インクルードを自動作成します。

6b. 既存のテストを更新する
SAPWrite(action="update", type="CLAS", name="<class_under_test>", include="testclasses", source="<full_updated_testclasses_include>", transport="<transport>")

既存のローカルテストメソッドを少し修正する場合は、インクルードの作成後にメソッド編集を使用します。

SAPWrite(action="edit_method", type="CLAS", name="<class_under_test>", method="<ltc_class>~<failing_method>", source="<fixed_method_body>")

注: 更新アクションでは、transport の指定を推奨しますが、必須ではありません。明示的に transport を指定しない場合、ARC-1 はロックによって提供された corrNr を自動的に引き継ぎます。

6c. テスト対象クラスを有効化する
SAPActivate(type="CLAS", name="<class_under_test>")

有効化すると、行番号を含む詳細なエラー/警告メッセージを含む、構造化された応答が返されます。これらを使って、問題の正確な箇所を特定してください。

6d. 単体テストを実行する
SAPDiagnose(action="unittest", type="CLAS", name="<class_under_test>")
6e. 結果を報告し、失敗を修正する

ユーザーに次の内容を示します。

  • 成功/失敗/スキップしたテストの数
  • 失敗がある場合:アサーションメッセージと、失敗したテストメソッド
  • 失敗に対する修正案

テストが失敗した場合:

  1. 失敗メッセージを分析します
  2. モックの設定、アサーション、テスト対象クラス(CUT)のロジックのどれに問題があるかを判断します
  3. メソッド編集を使用して、該当するテストメソッドを修正します。
    SAPWrite(action="edit_method", type="CLAS", name="<class_under_test>", method="<ltc_class>~<failing_method>", source="<fixed_source>")
    
  4. 再度有効化して実行します

エラー処理

よくある問題と修正方法
エラー原因修正方法
class_setup が失敗するCUT のインスタンス化が誤っています。コンストラクターのパラメーターが不足しているか、型が誤っていますCUT のコンストラクターシグネチャを確認し、必要な依存関係がすべて注入されていることを確認します
モックの注入に失敗するコンストラクターパラメーターの型が一致していません。モックが期待されるインターフェースを実装していませんインターフェース名を確認し、CUT がインターフェースではなく具象クラスを要求しているかを確認します
アサーションが失敗する期待値またはテストデータが誤っていますCUT メソッドのロジックを再確認し、期待値または入力データを調整します
有効化エラー生成されたコードに構文エラーがあります。タイプミス、誤った型、変数宣言の欠落などです有効化エラーを読み、構文を修正してから再度有効化します
cl_abap_testdouble が見つからないシステムでテストダブルフレームワークを利用できません(古いリリースの場合)代わりにローカルテストダブルクラスのパターンを使用します
CUT にメソッドが見つからないメソッドがプライベートまたは保護されています。テストから呼び出せませんパブリックメソッドのみをテストします。保護されたメソッドの場合は FRIENDS 句を使用します
testclasses/CCAU インクルードが見つからない新規クラスには ABAP Unit インクルードがまだありませんSAPWrite(action="update", type="CLAS", include="testclasses", source=...) を使用します。ARC-1 が自動的に初期化します
edit_method でメソッドが見つからないローカルの ltc_* メソッドがまだ存在しないか、誤ったローカルクラス名が使用されていますまず update include="testclasses" で testclasses インクルード全体を書き込み、その後、既存のメソッドに edit_method を使用します

注記

BTP とオンプレミスの違い
  • BTP:ローカルの ltc_* テストクラスをカスタム Z*/Y* クラスに記述します。ABAP Cloud 構文を使用する必要があり、従来の ABAP ステートメントは使用できません。モックに使用できるのは、リリース済み API のみです。cl_abap_testdouble を利用できます。コンストラクターインジェクションが標準的なパターンです。
  • オンプレミス:制限なく柔軟に利用できます。従来の ABAP を使用できます。cl_abap_testdouble は 7.51 以降で利用できます。それ以前のシステムでは、ローカルテストダブルクラスを使用します。
このスキルでは対応しないこと
  • CDS 単体テストではありません:CDS エンティティには、generate-cds-unit-test スキルを使用します(CDS Test Double Framework を使用します)。
  • 統合テストではありません:このスキルでは、エンドツーエンドのフローではなく、依存関係をモックした分離された単体テストを生成します。
  • パフォーマンステストではありません:パフォーマンス分析には ABAP profiler(SAPDiagnose)を使用します。
  • RAP ビヘイビアーテストではありません:RAP BO のテストダブルには、別のパターンが必要です。
このスキルを使用する場面
  • クラスのパブリックメソッドに、複雑なビジネスロジックが含まれている場合
  • クラスが依存性注入(インターフェースを指定したコンストラクターパラメーター)を使用している場合。モックに適しています。
  • ユーザーが TDD の出発点としてテストカバレッジを求めている場合
  • クラスをリファクタリングする際に、変更前に回帰テストを作成したい場合
  • パブリックメソッドのないクラスや、単なるデータコンテナー(構造体/定数)には使用しないでください。Step 2 の分析でテスト可能なシナリオ(分岐、エラーパス、意味のある戻り値)が見つからない場合は、次のようにユーザーに警告してください。「このクラスには、テスト対象となる複雑なパブリックメソッドがありません。ここで単体テストを行う価値があるか検討してください。」

コメント