7 Oracle JETアプリケーションのテストおよびデバッグ

クライアント側アプリケーション用の推奨されるテストおよびデバッグ・ツール・セットを使用して、Oracle JET Webアプリケーションをテストしてデバッグします。

Oracle JETアプリケーションのテスト

テストは、回帰を防ぎ、テスト可能な関数、モジュール、クラスおよびコンポーネントで構成されるアプリケーションの作成を促すことによって、複雑なOracle JETアプリケーションを迅速かつ確実に構築するのに役立ちます。

アプリケーションの開発サイクルで、できるだけ早くテストを記述することをお薦めします。テストを遅らせるほど、アプリケーションの依存関係が増え、テストを開始するのが難しくなります。

テスト・タイプ

Oracle JETアプリケーションをテストする際に考慮する必要がある主なテスト・タイプは3つあります。

  1. ユニット・テスト
    • ユニット・テストでは、特定の関数、クラスまたはコンポーネントへのすべての入力が予期される出力またはレスポンスを生成していることを確認します。

    • これらのテストは、通常、UIレンダリング、ネットワーク・リクエストまたはその他の環境上の懸念を伴わない自己完結型のビジネス・ロジック、コンポーネント、クラス、モジュールまたは関数に適用されます。

      RESTサービスAPIは個別にテストする必要があることに注意してください。

    • ユニット・テストでは、コンポーネントの実装の詳細と依存関係を認識し、テストしたコンポーネントの分離に焦点を当てます。

  2. コンポーネント・テスト
    • コンポーネント・テストでは、個々のコンポーネントが操作可能で、期待どおりに動作できることを確認します。これらのテストでは、ユニット・テストよりも多くのコードがインポートされ、より複雑になり、実行に時間がかかります。

    • コンポーネント・テストでは、コンポーネントのプロパティ、イベント、提供されるスロット、スタイル、クラス、ライフサイクル・フックなどに関連する問題を捕捉します。

    • これらのテストは、コンポーネントの実装の詳細を認識しません。コンポーネントとシステム全体の統合をテストするために、モックアップは最小限に抑えます。

      コンポーネント・テストで子コンポーネントをモックアップしないでください。かわりに、ユーザーがコンポーネントを操作する(たとえば、要素をクリックするなど)ようにコンポーネントと相互作用するテストで、コンポーネントとその子の間の相互作用をチェックします。

  3. エンドツーエンド・テスト
    • エンドツーエンドのテスト(データベースまたはその他のバックエンド・サービスの設定を含むことが多い)では、複数のページにまたがる機能を確認し、本番構築されたJETアプリケーションに対して実際のネットワーク・リクエストを行います。

エンドツーエンドのテストは、個々のコンポーネントだけでなく、アプリケーション全体の機能をテストすることを目的としています。したがって、Oracle JETアプリケーションの特定のコンポーネントをテストする場合は、ユニット・テストおよびコンポーネント・テストを使用します。

ユニット・テスト

ユニット・テストは、最初に実行する最も包括的なテスト形式です。

ユニット・テストの目的は、ソフトウェア・コードの各ユニットが正しくコーディングされ、期待どおりに動作し、関連するすべての入力に対して予想される出力を返すことを確認することです。単位は、アプリケーションのソース・コード内の関数、メソッド、モジュール、オブジェクトまたはその他のエンティティです。

ユニット・テストは、コードの最下位レベルを実行および検証し、個々のエンティティを分離してテストするために作成された小規模で効率的なテストです。機能を分離することで、テスト対象のユニットに関連しない外部依存関係を削除し、障害の原因の可視性を高めます。

ユニット・テストは、コンポーネントのパブリック・アプリケーション・プログラミング・インタフェース(API)と対話し、可能なかぎり多くのコンポーネントのコード・パスを実行するために、必要な多くの異なるテスト・データの組合わせをAPIに渡します。これには、コンポーネントのプロパティ、イベント、メソッド、およびスロットのテストが含まれます。

作成するユニット・テストは、次の原則に従う必要があります:

  • 記述しやすい: ユニット・テストはテストの中心です。したがって、多くのテストが記述されるため、通常は簡単に記述できる必要があります。標準のテスト・テクノロジ・スタックと推奨される開発環境を組み合せることで、テストが簡単かつ迅速に記述されます。
  • 読みやすい: 各テストの意図はコメントのみでなく明確に文書化する必要がありますが、コードもその目的を簡単に解釈できるようにする必要があります。障害が発生し、デバッグが必要な場合に備えて、テストを読みやすくしておくことが重要です。
  • 信頼できる: テストは、コンポーネント・コードにバグがない場合は常に合格し、真のバグや未実装の新しい動作がある場合にのみ失敗するようにする必要があります。また、テストは、実行順序に関係なく確実に実行される必要もあります。
  • 高速: テストは迅速に実行され、問題をすぐに開発者に報告できる必要があります。テストの実行が遅い場合は、外部システムに依存しているか、外部システムと対話していることを示している可能性があります。
  • 個別: テストでは、すべてのユニットが正しく検証されていることを確認するだけでなく、障害発生時のバグの検出を支援するために、可能なかぎり小さな作業単位を実行する必要があります。各ユニット・テストでは、個々のテスト・ケースで、検証するコードの単一の属性を個別にターゲットとする必要があります。
  • 独立: 何よりも、ユニット・テストは相互に独立しており、外部の依存関係がなく、実行される環境に関係なく一貫して実行できる必要があります。

ユニット・テストは、結果に影響を及ぼす可能性のある外部変更からユニット・テストを保護するために、コンポーネントによって完全に所有されているコードの検証のみに焦点を当て、そのコンポーネントの外部の動作を検証しないようにします。外部の依存関係が必要な場合は、モックを使用してそのかわりにすることを検討してください。

コンポーネント・テスト

コンポーネント・テストの目的は、個々のコンポーネントが仕様どおりに動作し、操作できることを確認することです。コンポーネントが正しい入力を受け入れ、正しい出力を生成していることの検証に加えて、コンポーネント・テストには、コンポーネントのプロパティ、イベント、スロット、スタイル、クラス、ライフサイクル・フックなどに関連する問題のチェックも含まれます。

コンポーネントは多数のコード単位で構成されているため、コンポーネント・テストは複雑で、ユニット・テストよりも実行に時間がかかります。しかし、避けることはできません。コンポーネント内の個々のユニットは単独では機能しても、一緒に使用すると問題が発生する可能性があるからです。

コンポーネント・テストはクローズボックス・テストの一種です。つまり、テストでは基礎となるコードの詳細を考慮せずにプログラムの動作を評価します。開発後すぐにコンポーネントのテストを開始する必要がありますが、テスト対象のコンポーネントは、まだ開発されていない他のコンポーネントに依存する場合があります。開発ライフサイクル・モデルに応じて、外部の影響を防ぐために、コンポーネント・テストをシステム内の他のコンポーネントから分離して実行できます。

コンポーネントが依存するコンポーネントがまだ開発されていない場合は、実際のコンポーネントではなくダミー・オブジェクトを使用します。これらのダミー・オブジェクトはスタブ(関数と呼ばれます)とコントローラ(関数と呼ばれます)です。

テスト・レベルの深さに応じて、小規模コンポーネント・テストと大規模コンポーネント・テストの2つのタイプのコンポーネント・テストがあります。

コンポーネント・テストを他のコンポーネントから分離して実行する場合、小規模コンポーネント・テストと呼ばれます。小規模コンポーネント・テストでは、コンポーネントの他のコンポーネントとの統合は考慮されません。

コンポーネントを他のコンポーネントから分離せずにコンポーネント・テストを実行する場合、一般に、大規模コンポーネント・テストまたはコンポーネント・テストと呼ばれます。これらのテストは、コンポーネントの機能のフローに依存関係があり、分離できない場合に実行されます。

エンドツーエンド・テスト

エンドツーエンド・テストは、ソフトウェア製品の動作を最初から最後まで調べることで評価する方法です。このアプローチでは、アプリケーションが意図したとおりに動作することを検証し、すべての統合コンポーネントが相互に正しく機能することを確認します。また、エンドツーエンドのテストでは、最適なパフォーマンスを実現するために、製品のシステムの依存関係が定義されます。

エンドツーエンド・テストの主な目的は、実世界のシナリオをシミュレートし、システムとそのコンポーネントが適切に統合されているか、データの一貫性が保たれているかを評価することで、エンド・ユーザー・エクスペリエンスを再現することです。この方法では、ユーザーの視点からシステムのパフォーマンスを検証できます。

エンドツーエンドのテストは、広く採用され、信頼性の高い手法であり、次の利点があります。
  • 包括的なテスト・カバレッジ
  • アプリケーションの正確性の保証
  • 市場投入までの時間の短縮
  • コストの削減
  • バグの特定
現代のソフトウェア・システムは相互接続が進んでおり、様々なサブシステムが存在するため、それらに障害が発生するとシステム全体に悪影響を及ぼす可能性があります。エンドツーエンド・テストは、次の方法でこれらのリスクを回避するのに役立ちます:
  • システムのフローの確認
  • テスト領域のカバレッジの増加
  • サブシステムに関連する問題の特定
エンドツーエンドのテストは、様々な利害関係者にとって有益です:
  • 開発者は、テストの責任を軽減できるため、エンドツーエンド・テストを高く評価しています。
  • テスターは、実世界のシナリオをシミュレートして潜在的な問題を回避するテストを作成できるため、有用性を実感しています。
  • マネージャは、エンドツーエンドのテストによって、失敗するテストがエンド・ユーザーに与える影響を理解できるので、メリットがあります。
エンドツーエンドのテスト・プロセスには、次の4つのステージがあります:
  1. テストの計画: 必要な主要タスク、スケジュールおよびリソースの概説
  2. テストの設計: テスト仕様の作成、テスト・ケースの識別、リスクの評価、使用状況の分析およびテストのスケジューリング
  3. テストの実行: テスト・ケースの実行および結果の文書化
  4. 結果の分析: テスト結果のレビュー、テスト・プロセスの評価、および必要に応じて追加のテストの実行
エンドツーエンド・テストには2つのアプローチがあります:
  • 水平テスト: この方法は複数のアプリケーションでのテストを伴い、多くの場合、単一のERP(エンタープライズ・リソース・プランニング)システムで使用されます。
  • 垂直テスト: このアプローチでは、テストが階層順に実行されるレイヤーでのテストが行われます。この方法は、複雑なコンピューティング・システムの重要なコンポーネントをテストするために使用され、通常はユーザーやインタフェースは関与しません。

エンドツーエンド・テストは通常、完成品およびシステムで行われ、各レビューは完成したシステムのテストとして機能します。システムが予期した出力を生成しない場合、または問題が検出された場合は、2回目のテストが実行されます。この場合、チームはデータを記録して分析し、問題の原因を特定して修正し、再テストする必要があります。

アプリケーションをエンドツーエンドでテストする際は、次のメトリックを考慮してください:
  • テスト・ケース準備ステータス: このメトリックは、計画されたテスト・ケースと比較して、現在準備されているテスト・ケースの進行状況を追跡するために使用されます。
  • テストの進捗追跡: テスト完了率およびテスト・ケースの合格/不合格、実行/未実行および有効/無効のステータスの最新情報を提供するために、テストの進捗状況を週単位で定期的にモニタリングします。
  • 不具合のステータスおよび詳細: 未解決およびクローズ済不具合のパーセントと、重大度および優先度別の不具合の内訳を毎週提供します。
  • 環境可用性: 毎日の稼働時間数とテスト予定時間に関する情報。

Oracle JETテスト・テクノロジ・スタックについて

Oracle JETアプリケーションをテストするための推奨スタックには、JestおよびPreactテスト・ライブラリが含まれます。

Jestは、独自のテスト・ランナーおよびアサーション機能が付属するJavaScript/Typescriptの一般的なテスト・フレームワークです。これは、コード・カバレッジとスナップショット・テストをサポートしており、モックを簡単に作成し、テストをパラレルに実行できるため、分離の維持が保証されます。

Jestは、シミュレートされたブラウザ環境としてjsdomを使用したNodeJSで実行されます。環境では何もレンダリングする必要がないため、これらのテストは非常に迅速に実行されます。これは、ヘッドレスで実行されるDOMの軽量なインメモリー実装です。Jestテストは、スタイル設定にCSSを必要とするものを除き、コンポーネントのほぼすべての側面を検証するのに適しています。jsdomはCSSを処理しないため、これらのテストを使用してCSSを検証しないでください。

Preactテスト・ライブラリは、実装の詳細に依存することなく、Preactコンポーネントの動作をアサートするテストを簡単に記述できる一連のユーティリティ関数を提供します。これは、UI中心のテスト・アプローチを促進します。コンポーネントは完全なレンダリング・ライフサイクルを経由できます。ライブラリには、DOM内の要素を見つけるための問合せ関数と、それらと対話するためのユーザー・イベント・シミュレーション・ライブラリが用意されています。

Preactテスト・ライブラリの関数は、仮想DOMではなく、Preactによってレンダリングされる実際のDOM要素と連携動作するため、テストはユーザーがアプリケーションと対話し、ページ上の要素を検出する方法に類似します。

UI自動化テストでは、Selenium WebDriverOracle® JavaScript Extension Toolkit (Oracle JET) WebDriverとともに使用することをお薦めします。

テスト用のOracle JETアプリケーションの構成

ojet add testing CLIコマンドを使用して、JETコンポーネントのテストに必要なフレームワーク、依存関係およびライブラリを設定することで、Oracle JETアプリケーションにテスト機能を追加します(JestおよびPreactテスト・ライブラリを含む)。

ojet add testingコマンド

アプリケーションのルート・ディレクトリのターミナル・ウィンドウからコマンドを実行します。アプリケーションのテスト環境を構成したら、JestとPreactテスト・ライブラリを使用してアプリケーションでテストの作成および実行を進めることができます。

ojet add testingコマンドによって実行される構成では、アプリケーション内にテストにとって重要ないくつかのディレクトリ、依存関係およびファイルが作成されます。

たとえば、構成によって、test-configフォルダがアプリケーションのルート・ディレクトリに追加されます。これには、テストに必要な2つのファイル(jest.config.jsおよびtestSetup.ts)が含まれます。

ファイルtestSetup.tsは、コンパイルおよびトランスパイルされたasync関数のランタイム・サポートをインポートしますが、jest.config.jsはJestの構成ファイルです。

さらに、既存のコンポーネントでテスト・ファイルがチェックされます。テストを含むファイル(仕様ファイルと呼ばれる)の拡張子は、ツールおよびJestテスト構成で認識されるように、.spec.tsxにする必要があります。仕様ファイルがコンポーネントにない場合、それらが挿入されます。仕様ファイルは、componentsフォルダの__tests__フォルダ内にあります(src/components/<component-name>/__tests__など)。このフォルダには、コンポーネント用に書き込むテスト・ファイルが保持され、デフォルトでは、仕様ファイル<component-name>.spec.tsxを使用して作成されます。

ノート:

プロジェクトでadd testingコマンドを実行した後に新しいコンポーネントまたはパックをコマンドラインから作成すると、__tests__フォルダおよび仕様ファイルがデフォルトで挿入されます。

テスト・デモ

ここでは、ojet add testingコマンドを使用して、Oracle JETアプリケーションのテスト環境を構成する方法を示します。テストを有効にした後、サンプル・アプリケーションでユニット・テストとコンポーネント・テストを実行します。

  1. 最初に、basicテンプレートを使用して新しいOracle JETアプリケーションを作成します。作業ディレクトリでターミナル・ウィンドウを開き、コマンドnpx @oracle/ojet-cli create vdomTestApp --template=basic --vdomを実行します。
  2. cd vdomTestAppと入力してアプリケーションのルート・ディレクトリに移動し、コマンドnpx ojet create component hello-worldを入力して新しいコンポーネントを作成します。
  3. ./VDOMTestApp/src/components/content/index.tsxファイルを開き、新しく作成されたHelloWorldコンポーネントをインポートして表示します:
    import { HelloWorld } from "hello-world/loader"
    
    export function Content() {
      return (
        <div class="oj-web-applayout-max-width oj-web-applayout-content">
          <HelloWorld />
        </div>
      );
    };
  4. npx ojet serveコマンドを入力してブラウザでアプリケーションを実行し、アプリケーションが実行されて新しいコンポーネントがレンダリングされることを確認します。コンポーネントをテストするには、まずOracle JET CLIによってコンポーネントがビルドされる必要があるため、これはテストに必要なステップでもあります。


    実行中のアプリケーションでレンダリングされるHelloWorldコンポーネント

    ノート:

    実行中のアプリケーションで表示されるテキスト"Hello from hello world"は、HelloWorldコンポーネントのmessageプロパティを介してアプリケーションのコンテンツに渡され、コンポーネントが動作していることを確認できます。
  5. アプリケーションのルート・ディレクトリのターミナル・ウィンドウからnpx ojet add testingコマンドを実行します。

    テスト環境の構成に加えて、アプリケーションのディレクトリ構造で、test-configフォルダがルート・ディレクトリに追加され、__tests__フォルダが/src/components/hello-worldディレクトリに追加されたことがわかります。

    __tests__フォルダ内には仕様ファイルhello-world.spec.tsxがあり、これはデフォルトで、レンダリングを検証するHelloWorldコンポーネントのコンポーネント・テストを使用して作成されます。Jestはテスト・ケースとアサーション(describe()test()およびexpect(true).not.toBeUndefined;)を提供しますが、Preactテスト・ライブラリのrender()関数はアプリケーションのコンポーネントのレンダリングに使用されます。

    package.jsonファイルを参照すると、JestテストでOracle JET Web要素を使用できるJestプリセットや、2つの便利なスクリプト(testおよびtest:debug)など、追加されたテスト依存関係を確認できます。

  6. コンポーネント・テストに加えて、ユニット・テストを実行します。最初に、テストする関数を指定する必要があります。

    コード・エディタで./VDOMTestApp/src/components/hello-world/hello-world.tsxファイルを開き、ファイルの下部に、2つの数値の合計を返す次の関数を挿入します。ファイルを保存します。

    export const sum = (a: number, b: number) => {
      return a + b;
    }
  7. hello-world.spec.tsxファイルを開き、ファイルの上部に、sum関数のimport文を追加します: import { sum } from 'hello-world/hello-world'
  8. hello-world.spec.tsxファイルの下部に、sum関数用の次のユニット・テストを追加します。ファイルを保存します。
    it('The sum is 10', () => {
      expect(sum(6, 4)).toBe(10)
    })
  9. ターミナル・ウィンドウの./VDOMTestApp/ディレクトリから次のコマンドを実行して、アプリケーションのプロジェクト・ソースをビルドします

    npx ojet build

  10. テストを実行します。コマンドラインにnpm run testを入力し、ターミナル・ウィンドウの結果を確認します。HelloWorldコンポーネントでのテストの実行結果

Oracle JETアプリケーションのデバッグ

Oracle JET WebアプリケーションはJavaScriptまたはTypescriptで記述されたクライアント側のHTML5アプリケーションであるため、好みのブラウザのデバッグ機能を使用できます。

Webアプリケーションのデバッグ

Oracle JETアプリケーションをデバッグするには、ソース・コード・エディタおよびブラウザの開発者ツールを使用します。

Chrome、Edge、Firefoxなどの広く使用されているブラウザ用の開発者ツールには、ブラウザで実行されるOracle JETアプリケーションの検査およびデバッグに役立つ様々な機能が備わっています。これらの開発者ツールの使用方法の詳細は、ご使用のブラウザのドキュメントを参照してください。

デフォルトでは、ojet buildおよびojet serveコマンドはOracle JETライブラリのデバッグ・バージョンを使用します。(ojet buildまたはojet serveコマンドに--releaseパラメータを追加して) Oracle JETアプリケーションをリリース・モードで構築または提供する場合、アプリケーションでは縮小バージョンのOracle JETライブラリが使用されます。リリース・モードで構築したOracle JETアプリケーションをデバッグする場合、--optimize=noneパラメータを使用すると、改行と空白が保持されて、縮小された出力が読みやすくなります:

ojet build --release --optimize=none
ojet serve --release --optimize=none

--optimize=noneパラメータを使用しない場合、ブラウザの開発者ツールには、縮小されたソース・ファイルを「プリティ・プリント」して読みやすくするためのオプションが用意されています。

また、アプリケーションのデバッグにさらに役立つブラウザ拡張機能をインストールできる場合もあります。

最後に、Visual Studio Codeなどのソース・コード・エディタを使用する場合は、Oracle JETアプリケーションの開発およびデバッグを支援するために提供されているデバッグ・ツールについて理解してください。

Preact開発者ツールの使用

Preactブラウザ拡張機能をインストールすると、仮想DOMアプリケーションのデバッグ時に、ご使用のブラウザの開発ツールに追加のデバッグ・ツールを提供できます。

Preactは、https://preactjs.github.io/preact-devtools/で様々なブラウザ拡張機能のダウンロード・リンクを提供しています。

ブラウザの拡張機能をインストールしたら、アプリケーションのappRootDir/src/index.tsファイルに1行目としてpreact/debugのインポート文を含める必要があります:
import 'preact/debug';
import './components/app';

仮想DOMアプリケーションの作成時に次のインジェクタ・トークンを含めることで、仮想DOMアプリケーションがデバッグ・モード(ojet buildおよびojet serveのデフォルト・オプション)でビルドまたは提供される際、Oracle JETによってこのインポートを含める処理が行われます:

// injector:preactDebugImport
// endinjector
import './components/app';

その結果、Oracle JETのインジェクタ・トークンによってインポート文がデバッグ・モードでのみ含まれることが保証されるため、デバッグ・モードでアプリケーションをビルドまたは提供するときにimport 'preact/debug'文を含めたり、リリース用にビルドまたは提供するときに削除したりする必要はありません。

仮想DOMアプリケーションをデバッグ・モードで提供する場合(ojet serveのデフォルト・オプション)、ブラウザの開発者ツールに追加のタブ「Preact」が表示されます。次のイメージでは、ChromeブラウザのDevToolsに「Preact」タブが表示されています。

コンポーネントの階層を表示し、コンポーネントを選択して検査し、仮想DOMアプリケーションに関する問題のデバッグに役立つ他のアクションを実行できます。

このイメージについては周囲のテキストで説明しています

--release引数を使用して仮想DOMアプリケーションをリリース・モードでビルドまたは提供する場合、Oracle JETはPreact DevToolsをインポートしません。デバッグ・モードでPreact DevToolsを使用しない場合は、トークンを削除します。

また、Oracle JETでは、アプリケーションが作成されるとき、アプリケーションのappRootDir/src/path_mapping.jsonファイルに次のエントリが含められます。ここで説明されているPreact拡張機能を使用するには、これらのエントリが必要です。

. . . 
"preact/debug": {
      "cdn": "3rdparty",
      "cwd": "node_modules/preact/debug/dist",
      "debug": {
        "src": [
          "debug.umd.js",
          "debug.umd.js.map"
        ],
        "path": "libs/preact/debug/dist/debug.umd.js",
        "cdnPath": "preact/debug/dist/debug.umd"
      },
      "release": {
        "src": [
          "debug.umd.js",
          "debug.umd.js.map"
        ],
        "path": "libs/preact/debug/dist/debug.umd.js",
        "cdnPath": "preact/debug/dist/debug.umd"
      }
    },
    "preact/devtools": {
      "cdn": "3rdparty",
      "cwd": "node_modules/preact/devtools/dist",
      "debug": {
        "src": [
          "devtools.umd.js",
          "devtools.umd.js.map"
        ],
        "path": "libs/preact/devtools/dist/devtools.umd.js",
        "cdnPath": "preact/devtools/dist/devtools.umd"
      },
      "release": {
        "src": [
          "devtools.umd.js",
          "devtools.umd.js.map"
        ],
        "path": "libs/preact/devtools/dist/devtools.umd.js",
        "cdnPath": "preact/devtools/dist/devtools.umd"
      }
    },