mcpField
M
modernize-test-starter

modernize-test-starter

github.com/ui5テスト・品質44165 回インストール1 週間前に更新
リポジトリ

modernize-test-starter について

UI5 開発者向けに、ユニットテストと OPA5 テストを Test Starter 構成へ移行するスキルです。旧構成の検出、ファイルと設定の変更、移行後の検証を扱います。

主な機能

  1. 01ユニットテストと OPA のパターン検出
  2. 02Test Starter のスイート設定作成
  3. 03ユニットテストと OPA ジャーニーの変換
  4. 04条件に応じた iframe 移行
  5. 05構成と残存ファイルの確認

活用シーン

  1. 01UI5 プロジェクトの旧テスト構成の刷新
  2. 02OPA5 ランチャーの Test Starter 移行
  3. 03変換後に残ったファイルの特定

インストール

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

$npx skills add https://github.com/ui5/plugins-coding-agents --skill modernize-test-starter

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
linter-invisible-patterns.md
pattern-a-modernization.md
pattern-b-modernization.md
pattern-u-iframe-migration.md
testsuite-configuration.md
scripts/
check-dangling-entries.js
check-dangling-entries.test.js
detect-cross-window-imports.js
detect-cross-window-imports.test.js
parse-testsuite.js
parse-testsuite.test.js

詳細

Test Starter へのモダナイズ

このスキルでは、UI5 プロジェクトのテストインフラ全体(ユニットテストと OPA5 統合テストの両方)を、Test Starter の概念に合わせてモダナイズします。

参考:ブログ記事:テストセットアップの簡素化

モダナイズする理由

Test Starter は、テストごとの HTML ブートストラップを、単一の汎用テストページと宣言型の testsuite.qunit.js 設定に置き換えます。利点:定型的な HTML/JS をなくし、QUnit/Sinon/カバレッジのセットアップを自動的に処理し、CSP への準拠を確保して、一貫性のある設定を提供します。また、テストごとの分離と並列実行を可能にします。

Test Starter へのモダナイズを絶対に省略しないでください

OPA5 のテスト HTML ファイルは必ず変換できます。機械的なパターンに従っているためです。無効な言い訳:「複雑なモックサーバー」(ホストにあり、エントリーポイントにはありません)、「sap.ui.define のモンキーパッチ」(同様です)、「FLP サンドボックスのブートストラップ」(同様です)、「ファイルが多すぎる」(一括処理してください)、「複雑すぎる」(無効です)。

種類例対応
テストドライバーopa/<Area>/<TestName>.qunit.htmlTest Starter に変換
テストホストtest<ServiceName>.htmlそのまま維持(テスト対象アプリを iframe で実行)
テストスイートtestsuite.qunit.htmltestsuite.qunit.js に置き換え
開発用サンドボックスflpSandbox.html無視

唯一の実質的な技術的課題は、OPA5 のエントリーポイントで QUnit ブートストラップライブラリの読み込みに jQuery.sap.require() を使用している場合です。sap.ui.require に置き換えるか、Test Starter が自動的に読み込むことを記載してください。

前提条件

manifest.json → sap.app/id を読み取ります。これが名前空間です。

  • <NAMESPACE> — ドットをスラッシュに置換します(例:my.app → my/app)
  • <NAMESPACE-WITH-DOTS> — ドットを含む元の値です

フェーズ 0:検出

0.1 ユニットテストの確認

unitTests.qunit.html、unitTests.qunit.js、allTests.js、AllTests.js、または allTests.qunit.js を含む webapp/test/unit/ を探します。すべてのレガシー集約ファイルを特定します。これは、実際のテストロジック(QUnit.module、QUnit.test、assert.*)を含まず、sap.ui.require/sap.ui.define で他のテストモジュールを読み込むことだけを目的とする JS ファイルです。その内容は unitTests.qunit.js にインライン化し、ファイルは削除します。

0.2 OPA ランチャーと FLP サンドボックスの有無を分類

フェーズ 5b は、needsIframeMigration = (launcher === "in-window" AND flpSandbox === true) の場合に実行します。

node <skill-dir>/scripts/parse-testsuite.js --detect-launcher webapp/test > /tmp/launcher.json

{ "launcher": "iframe"|"in-window"|"mixed"|"none", "flpSandbox": true|false, "needsIframeMigration": true|false } を返します。

launcherflpSandbox対応
iframe任意パターン I — フェーズ 5 のみ。5b はスキップ
in-windowtrueパターン U — フェーズ 5 とフェーズ 5b
in-windowfalse通常のインウィンドウ — 5b はスキップし、iStartMyUIComponent はそのままにする
mixed任意停止してください。 MODERNIZATION_ISSUES.md に記録し、開発者に整合を依頼
none任意OPA テストなし — OPA フェーズをスキップ

パターン U とパターン B の組み合わせはサポートされていません。 needsIframeMigration === true で、フェーズ 0.3 がパターン B を報告した場合は停止し、開発者に伝えてください。

0.3 OPA テストを確認 — パターンを特定

パターン A —「単一 HTML + AllJourneys」:opaTests.qunit.html + AllJourneys.js オーケストレーター。 パターン B —「複数の個別 HTML ファイル」:webapp/test/opa/ 内の複数の *.qunit.html。

検出:

find webapp/test -name "AllJourneys.js" -o -name "AllJourneys.json"
find webapp/test/opa -name "*.qunit.html" -type f 2>/dev/null | wc -l
0.4 解析スクリプトを実行
node <skill-dir>/scripts/parse-testsuite.js <testsuite.qunit.html> <test-base-dir> <namespace>

出力:pattern(A/B)、summary(件数)、entries(モジュールから設定へのマッピング)、opaConfig(パターン A 用)。これを保存してください。以降の作業はこの内容に基づいて進めます。

0.5 ブートストラップのオーバーライドを報告
node <skill-dir>/scripts/parse-testsuite.js --scan-bootstrap-overrides webapp/test > /tmp/bootstrap-overrides.json

検出対象:sap.ui.define = ...、sap.ui.require = ...、defineModuleSync(...)。検出した各項目を MODERNIZATION_ISSUES.md に追加します。オーバーライドは書き換えないでください。報告することが成果物です。

フェーズ 1:testsuite.qunit.js を作成する(メイン)

すべてのテストを列挙します。ユニットテストは "unit/unitTests" に委譲し、OPA ジャーニーは個別に列挙します。パーススクリプトの出力から OPA エントリーを作成します。キーには、webapp/test/ からの相対パスから .qunit.js を除いたものを使用します。

sap.ui.define(function() {
    "use strict";
    return {
        name: "QUnit test suite for <NAMESPACE-WITH-DOTS>",
        defaults: {
            page: "ui5://test-resources/<NAMESPACE>/Test.qunit.html?testsuite={suite}&test={name}",
            qunit: { version: 2 },
            sinon: { version: 4 },
            ui5: { theme: "sap_horizon" },
            loader: {
                map: { "*": {
                    "sap/ui/thirdparty/sinon": "sap/ui/thirdparty/sinon-4",
                    "sap/ui/thirdparty/sinon-qunit": "sap/ui/qunit/sinon-qunit-bridge"
                }},
                paths: { "<NAMESPACE>": "../" }
            },
            coverage: { only: ["<NAMESPACE>"], never: ["<NAMESPACE>/test"] }
        },
        tests: {
            // ----- Unit Tests -----
            "unit/unitTests": { title: "Unit Tests" },
            // ----- OPA Integration Tests -----
            "integration/NavigationJourney": { title: "Navigation Journey" }
        }
    };
});

オプションの詳細な説明については、references/testsuite-configuration.md を参照してください。

重要な点:

  • page には必ず ui5:// プロトコルのプレフィックスを使用してください。
  • module プロパティは不要です。Test Starter がエントリーキーに .qunit を追加します。
  • {suite} と {name} のプレースホルダーは必須です。
追加のローダーパス

必須です。 すべてのテスト HTML ファイルから、リソースルートのマッピングをすべて抽出します。

grep -rh "data-sap-ui-resourceroots" webapp/test/ --include="*.html"

ドット区切りのキーをスラッシュ区切りに変換し、loader.paths に追加します。値はすべて Test.qunit.html(webapp/test/ 内)を基準として解決されるため、サブディレクトリ内の HTML ファイルについてはパスを計算し直してください。

2 つの HTML ファイルで同じキーに異なる値が定義されている場合は、メインの testsuite.qunit.html/opaTests.qunit.html の値を優先してください。少数派のテストには、テストごとの loader.paths オーバーライドを設定します。

フェーズ 2:Test.qunit.html を作成する

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <script src="../resources/sap/ui/test/starter/runTest.js"
        data-sap-ui-resource-roots='{ "test-resources.<NAMESPACE-WITH-DOTS>": "./" }'
    ></script>
</head>
<body class="sapUiBody">
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
</body>
</html>

フェーズ 3:testsuite.qunit.html を更新する

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>QUnit test suite for <NAMESPACE-WITH-DOTS></title>
    <script src="../resources/sap/ui/test/starter/createSuite.js"
        data-sap-ui-testsuite="test-resources/<NAMESPACE>/testsuite.qunit"
        data-sap-ui-resource-roots='{ "test-resources.<NAMESPACE-WITH-DOTS>": "./" }'
    ></script>
</head>
<body></body>
</html>

フェーズ 4:ユニットテストの JS ファイルを最新化する

4.0 最初に — 冗長なアグリゲーターを削除する

冗長なアグリゲーターとは、実際のテストロジックがなく、sap.ui.require([deps], function() { QUnit.start(); }) または読み込みのみの sap.ui.define だけを含むものです。QUnit.config.autostart = false と QUnit.start() は起動用のコードであり、テストロジックではありません。

対応: 読み込むテストモジュールを記録してから(unitTests.qunit.js 用)、.js ファイルと対応する .html ファイルの両方を直ちに削除します。

4.1 実際のテストファイルを変換して名前を変更する

実際のテストロジック(QUnit.module、QUnit.test、assert.*)を含むファイルでは、次の作業が必要です。

  1. .qunit.js のサフィックスに名前を変更します(例:App.controller.js → App.controller.qunit.js)。
  2. sap.ui.define に変換します(QUnit.config.autostart と Core.ready のラッパーを削除します)。
// Before (App.controller.js)
QUnit.config.autostart = false;
sap.ui.getCore().attachInit(function() {
    sap.ui.require(["my/app/model/formatter"], function(formatter) {
        QUnit.module("formatter");
        QUnit.test("formatValue", function(assert) { ... });
    });
});

// After (App.controller.qunit.js)
sap.ui.define(["my/app/model/formatter"], function(formatter) {
    "use strict";
    QUnit.module("formatter");
    QUnit.test("formatValue", function(assert) { ... });
});
4.2 unitTests.qunit.js アグリゲーターを作成する

削除したアグリゲーターに含まれていたものと、追加の .qunit.js ファイルを合わせて、実際のユニットテストモジュールをすべて直接列挙します。

sap.ui.define([
    "./controller/Main.qunit",
    "./model/formatter.qunit"
]);

ルール:相対パスには ./ を使用し、.js を付けずに .qunit サフィックスを含めます。

jsUnitTestSuite の変換

以前の testsuite.qunit.js が jsUnitTestSuite を使用していた場合は、フェーズ 1 ですでに置き換えられています。古い内容を削除してください。

フェーズ 5:OPA テストを最新化する

パターン A — 単一の HTML + AllJourneys

詳しい手順については、references/pattern-a-modernization.md を参照してください。

  1. AllJourneys.js から OpaSetup.js を作成します。Opa5.extendConfig とページオブジェクトのインポートを抽出します。OpaSetup では sap/ui/test/opaQunit をインポートしないでください。
  2. ジャーニーファイルの名前を .qunit.js に変更します。
  3. ジャーニーを更新し、副作用依存関係として "./OpaSetup" を追加します(相対パス)。
  4. autoWait のオーバーライドを処理します。ジャーニーごとに Opa5.extendConfig を設定してください。
  5. OpaSetup.js の testLibs 設定を維持します。
// OpaSetup.js (no opaQunit!)
sap.ui.define(["sap/ui/test/Opa5", "test-resources/<NAMESPACE>/integration/pages/App"
], function(Opa5) {
    "use strict";
    Opa5.extendConfig({ viewNamespace: "<APP-NAMESPACE>.view.", autoWait: true });
});

// Journey file (opaQunit here, OpaSetup relative)
sap.ui.define(["sap/ui/test/opaQunit", "sap/ui/test/Opa5", "./OpaSetup"
], function(opaTest, Opa5) {
    "use strict";
    // opaTest(...) calls
});
パターン B — 個別の HTML ファイルが複数ある場合

詳しい手順については、references/pattern-b-modernization.md を参照してください。

  1. Opa5.createPageObjects を呼び出すユーティリティモジュールを一覧にします。
  2. OpaSetup.js を作成し、ユーティリティのインポートと Opa5.extendConfig をまとめます。
  3. ジャーニーの名前を .qunit.js に変更し、OpaSetup の依存関係を追加します。
  4. パーススクリプトの autoWaitFalseFiles に基づいて autoWait のオーバーライドを処理します。
  5. 複数モジュールを含む HTML ファイルでは、モジュールごとに testsuite エントリーを 1 つ出力します(*Combined という名前を決して作らないでください)。references/pattern-b-modernization.md のステップ 6 を参照してください。

フェーズ 5b:ウィンドウ内の OPA を bare-Component iframe に移行する

フェーズ 0.2 で needsIframeMigration: true と報告された場合のみ実行します。

手順の詳細は references/pattern-u-iframe-migration.md を参照してください。概要:

  1. opaIframe.qunit.html と opaIframeBoot.js を作成する — bare-Component iframe です。HTML ではスタブ用に sap/ushell の sandbox.js を読み込み、sap-ushell-config は使用しません。Boot モジュールでは mockserver.init() → new ComponentContainer(...).placeAt("content") を実行します。CSP に準拠するため、data-sap-ui-oninit="module:..." を使い、インラインスクリプトは使用しません。
  2. arrangements/Common.js を書き換える — iStartMyApp を iStartMyAppInAFrame(...) に変更します。mockserver のインポートと _clearSharedData を削除します。
  3. journey を書き換える — iStartMyUIComponent({...}) を iStartMyApp() に変更します(hash/autoWait を引き継ぎます)。
  4. OpaSetup.js から親フレームの mockserver 初期化を削除する — iframe 内で起動するようになります。
  5. ウィンドウをまたぐコントロールのインスタンス化 — waitFor コールバック内の UI5 コントロールは、Opa5.getWindow().sap.ui.require(...) を使って解決する必要があります。OPA で安全な依存関係は親側に保持します:sap/ui/test/*、sap/ui/core/routing/History。確認コマンド:node <skill-dir>/scripts/detect-cross-window-imports.js <project-root>。
  6. ウィンドウをまたぐ jQuery/DOM — 裸の $(...)、document.*、window.* を Opa5.getJQuery()(...)、Opa5.getWindow().* に置き換えます。
  7. ルーティングヘルパー — プレーンな Component ルーターの hash を使用し、#app-tile&/ のプレフィックスは付けません。
  8. mockserver の sap-message エンベロープ — メッセージコレクターには POST ハンドラーの sap-message ヘッダーが必要です。
  9. ErrorHandler の null ガード — getElementsByTagName("message")[0].firstChild.data が null にならないようガードします。
  10. <NAMESPACE>/test/integration/opaIframe を loader.paths に登録しないでください。

項目 5~9 はプロジェクト固有です。参照ファイルに記載された検出用 grep に従って機械的に適用してください。

フェーズ 6:古いファイルを削除する

ユニットテストファイル
  • unitTests.qunit.html、legacyTests.qunit.html を削除します(古いブートストラップ)。
  • フェーズ 4.0 で特定された重複するアグリゲーターがすべて削除されていることを確認します。
OPA ファイル — パターン A
  • opaTests.qunit.html、AllJourneys.js、AllJourneys.json を削除します。
OPA ファイル — パターン U(needsIframeMigration の場合のみ)
  • 以前の試行で作成されている場合は flpSandbox.qunit.html を削除します。
  • iStartMyUIComponent を呼び出す journey が残っていないことを確認します。
OPA ファイル — パターン B
  • webapp/test/opa/ 以下の *.qunit.html をすべて削除します(件数は summary.totalActive と一致する必要があります)。
削除しないファイル
  • testsuite.qunit.html(更新済み)、Test.qunit.html(作成済み)

フェーズ 7:検証する

  1. 件数チェック:testsuite.qunit.js の OPA エントリー数が、解析スクリプトの OPA 合計数と一致すること。
  2. 参照切れエントリーチェック:node <skill-dir>/scripts/check-dangling-entries.js webapp/test — 終了コード 0 = OK。
  3. リンター:npx @ui5/linter — prefer-test-starter の警告が残っていないこと。
  4. 構造レビュー:Test.qunit.html → runTest.js、testsuite.qunit.html → createSuite.js、testsuite.qunit.js → unit delegate + OPA journey であること。すべてのユニットファイルで sap.ui.define を使用していること。OpaSetup.js が存在し、すべての journey がこれをインポートしていること。古い HTML が残っていないこと。
  5. パターン U の検証(needsIframeMigration の場合):
    • opaIframe.qunit.html と opaIframeBoot.js が存在し、インラインの <script> 本文も window["sap-ushell-config"] もないこと
    • grep -rn "iStartMyUIComponent\b" webapp/test → ヒット数が 0
    • grep -rn "#app-tile&/" webapp/test → ヒット数が 0
    • mockserver.init() が opaIframeBoot.js 内だけにあること
    • opaIframe/flpSandbox 用の loader.paths エイリアスがないこと
    • detect-cross-window-imports.js の終了コードが 0
    • ErrorHandler の XML 解析がガードされているか、問題として報告されていること

実例

例 A — パターン A(単一 HTML + AllJourneys)

名前空間:com.mycompany.myapp、OPA journey 4 件 + ユニットテスト 2 件。 変更後:testsuite.qunit.js → 5 エントリー(unit 1 件 + OPA 4 件)。AllJourneys.js → OpaSetup.js + 個別エントリー。削除対象:AllJourneys.json、opaTests.qunit.html、unitTests.qunit.html。

例 B — パターン B(個別の HTML ファイルが多数ある場合)

名前空間:com.mycompany.myapp、OPA ジャーニー 45 件+ユニットテスト 3 件。 変換後:testsuite.qunit.js → 46 エントリー(ユニットテスト 1 件+OPA 45 件)。OpaSetup.js = ユーティリティのインポートを統合したもの。3 件のジャーニーで autoWait: false を上書き設定。削除対象:HTML ファイル 46 件+unitTests.qunit.html。

重要な注意事項

  • runTest.js と createSuite.js:createSuite.js = テストスイートの概要。runTest.js = 個別のテスト用の Test.qunit.html。
  • .qunit.js のサフィックス規則:.qunit.js が必要なのはテストスイートのエントリーキーのみです。sap.ui.define で読み込む依存関係(ページオブジェクト、OpaSetup.js)には通常の .js を使用します。
  • 依存関係パス内の .qunit:"./FilterBar.qunit" は FilterBar.qunit.js に解決されます。サフィックスがない場合は、ファイルが見つかりません。
  • test-resources/ プレフィックス:webapp/test/ 以下の依存関係には、<NAMESPACE>/test/... ではなく test-resources/<NAMESPACE>/... を使用します。
  • 既存の <NAMESPACE>/test/ 依存関係を変換する:フェーズ 5 の後、すべてのテスト .js ファイルをスキャンし、test-resources/<NAMESPACE>/ に変換します。
  • 相対パスと test-resources/:同じディレクトリー内の場合は ./ を使用します。別のディレクトリーの場合は test-resources/<NAMESPACE>/... を使用します。
  • OpaSetup.js では sap/ui/test/opaQunit をインポートしないでください — 各ジャーニーファイルに記述します。
  • 副作用のあるインポートは依存関係配列の末尾に置きます — 先頭に追加すると、パラメーターの位置がずれます。

完了チェックリスト

#確認項目
1runTest.js を含む Test.qunit.html が存在する
2testsuite.qunit.html が createSuite.js を使用している
3testsuite.qunit.js に "unit/unitTests" とすべての OPA ジャーニーが含まれている
4不要な集約ファイルが残っていない
5古いテスト用 HTML ブートストラップが残っていない
6unitTests.qunit.js が実在するテストファイルのみを参照している
7OPA エントリー数が解析スクリプトの合計と一致している
8孤立エントリーのチェックに合格している(終了コード 0)
9ブートストラップの上書き設定が MODERNIZATION_ISSUES.md に報告されている(該当する場合)
10ランチャーと FLP が分類されている。mixed の場合は停止している
11needsIframeMigration の場合:iframe ファイルが存在し、インラインスクリプトと iStartMyUIComponent がない
12needsIframeMigration の場合:FLP ハッシュプレフィックスとローダーエイリアスがない
13needsIframeMigration の場合:ErrorHandler が保護されているか、問題として報告されている
14needsIframeMigration の場合:detect-cross-window-imports.js の終了コードが 0

関連スキル

  • fix-csp-compliance — 古い HTML のインラインスクリプトは CSP に違反します。Test Starter がこれらを削除します。
  • fix-linter-blind-spots — リンターが検出できない、実行時に問題を起こすパターンを検出します。
  • fix-js-globals (cases 1b/1c) — リンターが報告する sap.* グローバルを処理します。

コメント