Skip to main content

 

アプリケーションの開発

 

 

OutSystems

テスタビリティを考慮した開発

他のプログラミング言語と同様、OutSystemsにも開発とアーキテクチャに関するベストプラクティスが適用されます。基本的に、開発したコード/モジュール/アプリケーションの再利用性、保守性、拡張性を確保しておく必要があります。

OutSystems(UI、連携、またはコンポーネント)でテストの自動化を導入すると、テストが実行できるようにコードを開発するという考えによってこれらのベストプラクティスが強化されます。また、新しいベストプラクティスも加わります。

すべてのアプリケーションは、正確性を検証するテストが円滑に行われるように開発する必要があります。この全体的なコンセプトは、関係するテクノロジーから完全に独立したものです。

このセクションでは、テストが実行できるOutSystemsアプリケーションを実装する場合に考慮するいくつかのベストプラクティスについて説明します。

ドメインの分離

OutSystemsでは、モジュールによってビジネスコンセプトとビジネスロジックが構成されています。各モジュールは、その中でコンセプトやロジックを定義して「所有」しています。このような構成のおかげで、モジュールによって、そのコンセプトやロジックがそれぞれの内部のビジネスルールに基づいて正しく使用されるようになります。ドメイン駆動設計(DDD)の原則によってコード(およびテスト)の分離を進めることを強く推奨します。DDDでは、分離されたテクノロジー成果物のドメインに基づいて複雑なシステムを開発するようにを推し進めています。ベストプラクティスに基づき、OutSystemsではドメインをLifeTimeのチームにマッピングします。このチームは1つのビジネスドメインのアプリケーションを保有し、これらのアプリケーションのライフサイクルは他のチーム(ドメイン)から独立しています。ドメインは、水平方向または垂直方向のいずれかです。シナリオに応じて、ドメイン間の分離レベルを慎重に評価する必要があります。

OutSystemsのDDDにあまりなじみがなく詳細を知りたい場合は、主な注意事項や原則について説明している優れたコンテンツがすでにいくつかあります。必ず以下をご覧ください。

このDDDの原則の説明により、OutSystemsアプリケーションの実装の一般的なリスクを確認することができます。これらはアプリケーションのテスタビリティに影響を与えるため、さらに詳しく説明します。

アンチパターン #1: UIレベルのみでのバリデーション

以下の図では、Coreモジュールが所有するコンセプトを編集するためのフォームがUIモジュール内の画面に表示されます。変更を送信すると、対応する画面アクション内ですべてのバリデーションが実行された後、レコードを保存するアクションがCoreモジュールから呼び出されます。Coreアクション自体は、特定の入力に対するバリデーションを実行するのではなく、主にエンティティのCreateアクションのラッパーとして機能します。

アンチパターン #1 - UIのみでのバリデーション

Coreアクションをテストするときは、アクションに任意の値を入力することができ、データベースとの通信に問題がない限り常に成功します。このため、実行時にこのアクションを使用するテストは適切さを欠いてしまいます。 この問題を解決するには、すべてのビジネスバリデーションをCoreアクションに移動し、モジュールのビジネスルールに基づいて入力が有効であることを確認します。

Coreモジュールアクションは、テストが実行でき、独自にビジネスバリデーション/ルールを考慮に入れている必要があります。さらに、UIテストに依存せずに単独でテストを実行してバリデーション/ルールを確認できるようにします。UIレベルでは、バリデーションも実行する必要があります。ここでは、ユーザーインタラクションに関するバリデーションに重点を置き、ビジネスルールにはそれほど重点を置かないようにし、簡単な入力検証のフィードバックなどを行います。

つまり、UIレベルではユーザーによる入力のみの検証に重点を置く必要があります。以下の点を考慮します。

  • オートコンプリート入力オプション内に存在する値が入力されたか。

  • 必須項目がすべて入力されたか。

これに対し、Coreレベルではビジネスルールの検証が必要です。

  • オートコンプリートで選択されたオプションは現在の状況で有効であるか。

  • 送信された必須項目の値は現在の状況で有効であるか。

アンチパターン #2:UIレベルでのビジネスロジック

ページの変更を送信する画面アクションのフローには、ビジネスロジックが埋め込まれています。図の例では、マスターレコードが作成され、次に子レコードが作成または更新され、最後に通知が作成されます。

アンチパターン #2 - UIでのビジネスロジック

このロジック全体をテストする必要がある場合、以下の2つのいずれかが発生します。

  1. いずれかのテストをUIレベルで実行する必要があります。特にUIテストは一般的に管理が難しく実行に時間がかかるため、コストがかかってしまう可能性があります。

  2. テストロジック内でロジックを複製する必要があります。これは、画面アクションを変更するたびにテストを更新する必要があるため、当然望ましくありません。

この問題に対処する最適な方法は、テストが必要なロジックをCoreモジュールレベルのパブリックアクションでカプセル化して、それを画面アクションで使用する方法です。このようにすると、実行や管理にコストがかかるUIテストを実装せずに、コンポーネントのテストでビジネスロジックをテストすることができます。

アンチパターン #3:サポートされていないクロスドメイン参照

DDDでは、ドメイン間の参照にはサービスアクションとパブリックエンティティを使用することを推奨しています。

アンチパターン #3 - サポートされていないクロスドメインの依存関係

ドメイン間でパブリックエンティティを直接参照するかしないかというこの決定がテストデータの設定に影響を与える場合があるため、ここで説明する必要があります。

これに対処するは、いくつかの方法があります。クロスドメインエンティティが直接参照されている場合、モックデータを他のドメインから使用することはできません。この場合、ローカルドメインでのテスト設定作業で必要なデータを外部ドメインに直接作成してもかまいません。

しかし、それらのエンティティがREST/SOAP APIまたはサービスアクションでのみ参照されている場合、モックサービスを使用して外部ドメインからのデータをシミュレーションすることが可能になります。これにより、テストの独立性と信頼性が向上します。このシナリオでは、ローカルドメインでのテスト設定作業で必要なデータを外部ドメインに直接作成することは許容されません。その代わりに、モックサービスを使用してデータを取得します。

サービスAPIの分離

現在のドメインの外にあるREST/SOAP APIサービスを利用する必要がある場合は、常にラッパーモジュール内で利用する必要があります。そして、REST APIを使用するパブリックアクション一式をこのラッパーモジュールで公開し、APIにアクセスする必要があるすべてのモジュールがラッパーモジュールを経由する必要があります。

基本的に、OutSystems 11以降で使用できるサービスアクションは、同じ環境内で実行されている他のOutSystemsアプリケーションからのみ使用できるRESTリモート呼び出しです。これらを使用してアプリケーション間の疎結合の依存関係を作成し、OutSystems内のモノリスを分割することができます。これらは実行時はRESTサービスであるため、異なるドメイン間で参照されるサービスアクションにこの分離を適用することも理にかなっています。

このセクションでは、APIを使用してREST、SOAP、サービスアクションを参照します。

アンチパターン #1:複数のモジュール内での同じAPIの利用

あるOutSystems環境内に、同じAPIを利用するモジュールが複数あります。テスト実行のスコープ内で、テスト対象のアプリケーションを(APIが公開されている)リモートシステムから分離するためにモックサービスを使用する必要がある場合、この特定のAPIを利用するモジュールを1つ1つ変更する必要があります。言うまでもなく、モジュールの変更を忘れる可能性が高くなるため、これを管理するのは大変な作業となり、エラーも起こりやすくなります。

アンチパターン #1 - 複数のモジュール内での同じAPIの利用

これを解決する最適な方法は、APIの利用をラッパーモジュールに分離して、パブリックアクションでAPIメソッドを公開することです。APIにアクセスする必要があるコアモジュールは、ラッパーモジュールを介してアクセスします。その後、テスト実行のスコープ内で、モックサービスを参照する必要がある場合は、ラッパーモジュールの1か所のみで実行します。

REST APIの場合は、利用しているRESTの定義で使用可能なOnBeforeRequestイベントアクションを使用することができます。

  • SOAP APIの場合は、テストモード中に(EnhancedWebReferencesエクステンションの)SetWebReferenceURLアクションを使用してターゲットURLを上書きします。

  • サービスアクションの場合は、開発時にはパブリックアクションのように動作するため、サービスアクション呼び出しを実行する代わりにテストで想定される出力を返すロジックをラッパーで実装する必要があります。

モック戦略の詳細については、「連携先のモックサービス」をご覧ください。

Web UIのシミュレーション

ウィジェットの命名

Webアプリケーションでの自動UIテストは、特定のユーザージャーニーやアプリケーションが提供する機能を実現するために、アプリケーションの画面でのユーザーインタラクションをシミュレーションするいう方法で行われます。このシミュレーションは、ユーザーが画面要素で行う必要がある様々な手順をスクリプト化して行います。たとえば、ホームメニューのリンクをたどり、送信ボタンをクリックし、メール入力に「email@domain.com」と入力する、というような場合です。

つまり、UIのテストスクリプトではユーザーが操作する要素を一意に識別する必要があります。画面の要素を一意に識別する最も簡単な方法は、IDプロパティを使用する方法です。このため、OutSystemsアプリケーションの画面のスコープ内で、それらのウィジェットがService Studioで適切に識別されている必要があるということになります。

開発者は画面を作成するときに、入力、ボタン、リンクといったある程度のユーザーインタラクションがあるすべての要素のNameプロパティに、わかりやすい適切な値を割り当てるようにする必要があります。こうすることで、要素のIDアトリビュートの末尾が常にウィジェットのnameプロパティの値になるため、生成された画面コードで要素を識別しやすくなります。また、画面の特定の要素が適切に識別されていないことがUIテストでわかったときに、後々の余計な手間がかからなくなります。

最終的に、特定のUIテストで画面に追加要素が必要になり、その要素にも識別子が必要になるかもしれません。しかし、少なくとも先ほど説明したようにウィジェットに名前を付けておくことで、開発者が行わなければならない再作業を最小限に抑えることができます。このような手間を省くことがUIテスト実装時のワークフローの高速化につながります。

また、名前をセレクタの基礎として使用すると、同じ画面内での名前の衝突をプラットフォームで検出できます。これは、付加的なメリットです。2つの異なるウィジェットのNameプロパティを同じ値にすることはできないため、たとえば、2つ目のウィジェットの値にはサフィックスとして「2」を追加します。

それでもなお、OutSystemsでは複数のWebブロックを含む画面を作成することができるため、同じ画面内の2つの異なるWebブロックで同じ名前を使用した場合、Service Studioではこの名前の衝突に自動的に対処することはできません。これを考慮し、開発者がブロック内のウィジェットに名前を付けるときに、UIウィジェットの名前のプレフィックスとしてWebブロック名を含めることを推奨します。こうすることで、複数の異なるWebブロックを含む画面を作成する場合も名前が一意になるようにすることができます。

それでは、同じ画面上で同じWebブロックのインスタンスを複数配置する必要がある場合はどうでしょうか。このようなシナリオでは、画面に対して特定のIDを持つコンテナに各Webブロックをラップし、そのコンテナIDを使用して画面に対して一意となるように各Webブロックインスタンスを固定する必要があります。

通常、テストツールはXPathセレクタまたはCSSセレクタを使用して各UI要素を識別します。先ほど説明した方法でUI要素を識別する場合、これらのUIウィジェットにはすべてIDが設定されますが、Service Studio内でウィジェットに付けられた実際の名前が必ず末尾に付く「合成された」IDがOutSystemsプラットフォームによって生成されます。

以下は、特定の一意の名前が末尾に付くこれらのIDを識別する方法の簡単な例です。なお、名前は、Service Studioの「UserNameInput」という名前の入力に対するCSSセレクタとXPathセレクタの両方を意味します。

CSSセレクタ
input[id$=UserNameInput]
XPathセレクタ(1.0)
//input[substring(@id, string-length(@id) - string-length('UserNameInput') + 1) = 'UserNameInput']
XPathセレクタ(2.0)(通常、テストツールではサポートされていません)
//input[ends-with(@id,’UserNameInput’)]

拡張プロパティによる代替方法

Nameプロパティを使用して要素を識別する代わりに、「os-test-id」という名前の拡張プロパティを識別子として使用することができます。先ほどの例と同じ入力で、「os-test-id」拡張プロパティにService Studioの「UserNameInput」の値を追加します。プラットフォームによって生成されるHTMLコードでは、先ほど説明した名前/値のペアがアトリビュートに追加されます。

そして、以下のセレクタを使用して画面の要素を識別することができます。

CSSセレクタ
input[os-test-id=UserNameInput]
XPathセレクタ
//input[@os-test-id='UserNameInput']

この方法は画面内の要素の識別を完全に制御することができますが、欠点もあります。

まず、開発者はService Studioでウィジェットにこれらの拡張プロパティを追加することから始める必要があり、開発労力が増加してしまいます。ウィジェットに名前を付けるほうがごく自然な感じがするでしょう。

さらに、拡張プロパティについてはOutSystemsプラットフォームで衝突が検出されないため、開発者が衝突を確認する必要があります。複雑な画面では、この管理が特に難しい場合があります。

4 Layer Canvasへのテストのマッピング

4 Layer Canvas

4 Layer Canvas(4LC)は、サービス指向アーキテクチャの設計を簡素化するOutSystemsのアーキテクチャツールです。

4LCにより、再利用可能なサービスの正確な抽象化と、別個の機能モジュールの適切な分離が促進されます。共通のモジュールを再利用する複数のアプリケーションを開発したり管理したりするときに便利です。

各レイヤーでモジュールに実装される機能の性質上、これらのレイヤーを、各レベルで実装することが適切である機能テストのタイプにマッピングすることができます。

オーケストレーションレイヤー

機能

プロセス、ダッシュボード、ポータルホームページ。異なるアプリケーションからの情報を集約し、統合されたユーザーエクスペリエンスを提供します。

テスト

自動UI(エンドツーエンド)テスト(ユーザージャーニー)

多くの場合、オーケストレーションモジュールでは複数のアプリケーションに対応した画面を実装します。これらの画面は情報を重視したものであり、フォームはそれほど重視されません。これらの画面には、エンドユーザーレイヤー上に構築されたアプリケーションの画面へのリンクが含まれます。このため、このレイヤーの機能に対して実装されるテストは、複数のアプリケーション間の移動を要する完全なユーザージャーニーのUIテストになります。これらのテストでは、ナビゲーションとアプリケーションがお互いに及ぼし合う影響を検証します。

例:

  1. イントラネットポータルにアクセスする。
  2. Vacationsアプリに移動する。
  3. 1週間の休暇を送信する。
  4. Allocationアプリに移動する。
  5. 送信した週が休暇としてブロックされていることを確認する。

エンドユーザーレイヤー

機能

ユーザーインターフェイスおよびプロセス。コアサービスやライブラリサービスを再利用してユーザーストーリーを実装します。

テスト

自動エンドツーエンドテスト(UIテスト)

エンドユーザーモジュールでは、特定のビジネスドメインを持つ単一のアプリケーションに固有の画面を実装します。フォームがさらに重視される傾向にありますが、少なくとも情報/フォームが混在します。また、そのアプリケーションのビジネス機能にも重点が置かれます。したがって、このレイヤーの機能に対して実装されるテストは、そのアプリケーションに実装されているビジネスロジックを検証するUIテストになります。

例:

  1. Vacationsアプリにログインします。
  2. [Calendar]画面に移動します。
  3. 9月のある週を選択し、承認を申請します。
  4. Vacationsアプリにマネージャーのアカウントでログアウト/ログインします。
  5. 送信された休暇申請が[To Approve]リスト内にあることを確認します。

コアレイヤー

機能

ビジネスコンセプトに関連するサービス。再利用可能なエンティティ、ビジネスルール、ビジネスウィジェットをエクスポートします。

コアモジュールでは、ビジネスコンセプトとロジックを実装します。コアレイヤーでは水平方向の参照が可能であるため、コアモジュール間の階層が実際に構築されます。その結果、コアモジュールの階層内でのレベルに応じて様々なタイプのテストを実装することができます。レベルが低いほど単体テストに近くなり、レベルが高いほどエンドツーエンドテストに近くなります。後者の場合、複数のモジュールのコンセプトへの依存関係が含まれ、外部システムとの連携が含まれることもあります。

テスト

自動エンドツーエンドテスト(非UI)

非UIのエンドツーエンドテストでは、アプリケーションの画面を使用したり期待される出力をアサートしたりせずにOutSystemsコードを直接呼び出すことにより、ビジネスロジックを検証します。実際には、アプリケーションの複数のコンポーネントをテストします。このテストでは、2つの側面から調べることができます。

  1. 複数のビジネスアクションを論理的な順序で明示的に呼び出してユーザーがUIで実行する一連のアクションをシミュレーションし、最終的な結果をアサートします。

  2. 様々なモジュールの複数のコンセプトのオーケストレーションをカプセル化した単一のビジネスアクションを呼び出し、完全なユーザーインタラクションの検証を実現します。

自動APIテスト

APIテストでは、公開しているAPIの正確性を仕様に基づいて検証します。これには、外部システムが公開しOutSystemsプラットフォーム内で利用されるAPIと、OutSystemsモジュールが公開し外部の他のアプリケーションやシステムで利用されるAPIがあります。通常、これらのテストはAPIに対する入力とそれに対して期待される出力によって定義されます。

自動単体/コンポーネントテスト

単体/コンポーネントテストでは、下位のコードモジュールのビジネスロジックを検証します。下位のコードモジュールは自己完結型であり、他のコアモジュールに対する依存関係はほとんど(またはまったく)ありません。したがって、通常これらのモジュールには定義されているコンセプトに固有のビジネスロジックが含まれますが、他のコンセプトや上位のロジックのコンテキストはありません。このため、テストが実行できるOutSystemsアプリケーションの最小のビジネス要素となります。

ライブラリレイヤー

機能

フレームワークを拡張するビジネスに依存しないサービス。再利用性の高いアセット、UIパターン、外部システムへのコネクタ、ネイティブコードの連携が含まれます。

テスト

自動APIテスト

APIテストでは、公開APIの正確性を検証します。これには、外部システムが公開しOutSystemsプラットフォーム内で利用されるAPIと、OutSystemsモジュールが公開し外部の他のアプリケーションやシステムで利用されるAPIがあります。通常、これらのテストはAPIに対する入力とそれに対して期待される出力によって定義されます。

自動単体/コンポーネントテスト(非ビジネス)

定義のとおり、ライブラリモジュールはビジネスに依存しません。そのため、ビジネスロジックを含みません。しかし、あらゆるOutSystemsアプリケーションが利用する可能性がある再利用性の高いアセットを提供することから、これらのモジュールが提供する機能をテストすることは有益です。このようなライブラリの例として、ユーザーがアプリケーションで行ったすべてのアクションをAuditエンティティに記録する監査サブシステムなどがあります。

  • Was this article helpful?