APIテストを活用してマイクロサービスをテストする方法

マイクロサービスは、複雑なシステムを構築するためのアーキテクチャとして、開発コミュニティ内でも支持を広げています。マイクロサービスがアプリケーションアーキテクチャのあらゆる問題を解決する万能薬ではないことも理解されはじめていますが、依存関係とスケーリングに関する課題を持つアプリケーションにとっては、大きなメリットがあります。

マイクロサービスの採用が増加しているいっぽうで、マイクロサービスをどうテストするかという点で、試行錯誤もなされています。ThoughtWorksのToby Clemson氏は、マイクロサービスアーキテクチャに推奨されるテスト戦略を列挙するという素晴らしい仕事をしています(推奨されているさまざまなタイプのテストの概要については、Clemson氏の記事を参照してください)。しかし、そのようなさまざまな種類のテストをどのように構築し維持するかについては、まだ有力な定石がないのが現状です。

しかし、多くの点で、マイクロサービスアプリケーションをテストすること は、他のアーキテクチャを使用して構築されたアプリケーションをテストするのと変わりありません。 マイクロサービスでは、RESTやキューなどの既知のテクノロジーが使用されています。これらのテクノロジーには、すでに十分に確立されたテストツールとベストプラクティスがあります。マイクロサービス独自の課題は、アプリケーションを構成するサービスの数が膨大であることと、サービス間の依存関係です。さらに、依存先の他のマイクロサービスが利用できない、または適切に応答していない場合でも、各マイクロサービスが依然として適切に機能する必要がある点も課題として挙げられます。

マイクロサービスは、通常、オーケストレーションとリアクティブ(コレオグラフィー)という2つのパターンに従います。多くのマイクロサービスは、この2つを組み合わせた「ハイブリッド」アプローチを採用しています。この記事では、さまざまなパターンのマイクロサービスについて、自動テストを作成しようとしたとき発生する課題に対処するための戦略をいくつか提案します。ここでは、アプリケーション全体をテストするエンドツーエンドテストではなく、個々のマイクロサービスのテストに着目します。

オーケストレーション型マイクロサービスのテスト

オーケストレーションを使用するマイクロサービスは、外部サービスまたは依存先への1つまたは複数の明示的な呼び出しを行います。多くの場合、呼び出しは同期リクエスト-レスポンスフローを使用し、RESTベースのサービスにアクセスします。サービスが特定の順序で呼び出される必要がある場合、前のサービスへの呼び出しに対して応答が受信されるまで、後続のサービスへの呼び出しは行われません。1つのサービスが明示的に別のサービスを呼び出すため、サービスは密接に結合されています。

上記の例では、PortfolioマイクロサービスはAccountsおよびQuotesマイクロサービスに依存しており、これらのサービスもPortfolioマイクロサービスとともにテスト環境にデプロイする必要があるため、Portfolioマイクロサービスのテストの作成と実行は困難です。Quotesサービスは、リアルタイムの株価を取得するサードパーティのサービスに依存しており、外部サービスから返されるデータは常に変化しています。

サードパーティ製のサービスや別のチームによって開発されたサービスを利用していることで、テスト環境が非常に複雑になります。また、Portfolioサービスの予想外の動作(AccountsサービスやQuotesサービスが利用できない、レスポンスが遅い、または予期しないデータが返されるなど)もテストする必要があります。Portfolioマイクロサービスがさまざまなエラー条件を適切に処理できるかを検証するには、AccountsサービスおよびQuotesサービスに、さまざまな種類の予期しない応答をさせることが必要です。

サービス仮想化による課題解決

サービス仮想化を利用すると、AccountsおよびQuotesマイクロサービスの応答をシミュレートできます。サービス仮想化により、AccountsおよびQuotesマイクロサービスの仮想バージョンを定義し、それらをPortfolioマイクロサービスの実インスタンスと共にデプロイすることができます。マイクロサービスの仮想化は、他の種類のサービスやアプリケーションアーキテクチャの仮想化とさほど変わりません。たとえば、次のようになります。

これが完了すると、Portfolioマイクロサービスは、2つの依存先から独立してテストできます。

次の課題は、AccountsおよびQuotesサービスが予期しない動作を示す場合など、さまざまなケースに対応するさまざまな環境を設定することです。たとえば、AccountsサービスまたはQuotesサービスのいずれかのレスポンスが遅い場合、またはエラーを返す場合のPortfolioサービスの動作をテストするとします。これには、少なくとも5つの異なるテストセットを実行する必要があります。それぞれのテストでは、応答時間の遅延、エラー応答、および依存先サービスの動作が正常の場合と異常の場合を考慮して、異なる環境を設定します。

各テストの実行では、その設定のテストを実行する前に、環境を適切に設定する必要があります。この例では、5つの異なるテストを実行します。それぞれのテストには、固有の環境設定が必要です。ParasoftのContinuous Testing Platformに含まれるEnvironment Managerモジュールは、これらのさまざまな環境構成を管理できます。

このプロセスは、マイクロサービスアーキテクチャに固有のものではなく、一般的なサービス指向アーキテクチャや、少数のサービスにしか依存しないモノリシックアプリケーションでも同様の問題が発生します。しかし、マイクロサービスアーキテクチャでは、依存サービスの数が大幅に増加します。数十または数百のサービスがあるマイクロサービス環境では、さまざまなテストシナリオに対応するさまざまな環境設定を作成・管理し、プログラムによって切り替えられることが非常に重要であり、それができると時間と労力を大幅に削減できます。

オーケストレーション型マイクロサービスでのAPIの変更管理

マイクロサービスが拡大するにつれて、APIが変更されることは避けられません。APIの変更に伴って発生する重要な課題は、変更がサービスのコンシューマーに与える影響をどのように把握するかです。

開発中のマイクロサービスのAPIを変更すると、そのマイクロサービスを検証するテストは、APIの変更に合わせて更新する必要があります。逆に、仮想サービスを使用して依存先マイクロサービスをシミュレートしており、依存先マイクロサービスのAPIが変更された場合、仮想サービスを更新してAPIの変更を反映する必要があります。

多くのチームでは、OpenAPI、RAML、その他のサービス定義を使用して、マイクロサービスのAPIを記述しています。そのようなサービス定義を使用して、Parasoft SOAtestおよびParasoft Virtualizeに含まれる変更アドバイザーモジュールは、変更されたAPIを自動的に検出し、APIのフィールド追加/削除に合わせて既存の機能テストまたは仮想サービスを自動的にリファクタリングします。更新後のサービス定義を作成し、変更アドバイザーを適用すると、実際に変更を行う前に、変更がテストおよび仮想サービスに及ぼす影響を把握できます。変更が行われたら、変更アドバイザーを使用してすばやく簡単に既存の資産を更新してマイクロサービスの変更を反映することができます。

リアクティブ型マイクロサービスのテスト

マイクロサービスアーキテクチャの主な目的の1つは、コンポーネントを独立させることです。そうすると、サービスのデプロイ、スケーリング、更新が容易になります。しかし、オーケストレーションパターンを使用する場合、個々のマイクロサービスが他のマイクロサービスに直接依存しているため、この目的は完全には実現されません。これを解決する方法は、「リアクティブ」または「イベント駆動型」マイクロサービスとも呼ばれるコレオグラフィーパターンを使用することです。このパターンでは、マイクロサービスはお互いを直接参照しません。代わりに、他のマイクロサービスがサブスクライブしているイベントストリームにメッセージをプッシュします。

次の例を見てください。

この例で、たとえばPortfolioサービスが株式持ち高の追加を指示されたとします。すると、Accountsサービスを直接呼び出すのではなく、「Position Added」イベントストリームにイベントをパブリッシュします。AccountsマイクロサービスはPosition Addedイベントストリームをサブスクライブしているため、通知を受け取ります。Accountsサービスはユーザーが口座に十分な資金を持っているかを確認します。資金が十分であれば、ユーザーの口座の残高を減らし、イベントを「Account Updated」イベントストリームにパブリッシュします。ユーザーがアカウントに十分な資金を持っていない場合、エラーイベントを別のイベントストリームにパブリッシュすることができます(サンプルを簡略化するため、ここには表示されていません)。Portfolioマイクロサービスは、「Account Updated」イベントストリームをサブスクライブしており、Accountsマイクロサービスによって通知されたイベントを受け取ると、Accountsサービスからの確認に基づいてポートフォリオを更新します。

リアクティブ型アーキテクチャの非同期通信は、サービスが相互に非常に分離されているという利点をもたらします。個々のサービスのインスタンスは、他のマイクロサービスに影響を与えることなく、置き換え、再デプロイ、スケーリングが可能です。そのトレードオフは、イベントの非同期性によって、システムの実行方法とイベントの流れを理解するのが困難になるという点です。生成されるイベントの順序や種類によっては、システムが予期しない動作をする可能性があります。これは突発的動作として知られており、コレオグラフィー型マイクロサービスの開発とテストに固有の課題です。

非同期コマンド呼び出しパターン

広義のイベント駆動型マイクロサービスカテゴリに属する非同期メッセージングパターンには、ほかにもさまざまなものがあります。非同期コマンド呼び出しパターンは、非同期アクションを使用してマイクロサービスをオーケストレーションする必要がある場合に使用されます。この場合、1つのマイクロサービスが別のマイクロサービスを非同期的に呼び出しますが、2つ目のマイクロサービスが確実にメッセージを受け取ったことを保証しなければなりません。このパターンでは、通常、メッセージはキューを使用して交換されます。

このパターンを実装するためにマイクロサービスアーキテクチャで使用される一般的なフレームワークは RabbitMQ です。このパターンの具体例は、1つのマイクロサービスが第2のマイクロサービスが処理するイベントをパブリッシュし、2番目のマイクロサービスからの「リプライ」イベントの読み出しを待つ必要がある場合です。

Portfolioの例で考えてみましょう。REST APIコールがPortfolioマイクロサービスに持ち高を追加するよう指示します。Portfolioサービスは、Accountsマイクロサービスが処理するイベントをPosition Added キューにポストします。その後、AccountsサービスがリプライイベントをAccount Updatedキューにポストし、REST APIコールがイベントから受信したデータを返すことができるようになるのを待ちます。この例のテストシナリオを構成するには、次の2つの方法があります。

  1. 1 つめの方法は、必要なキューを持つ環境を作成し、Portfolioサービスだけをデプロイして、Accountsサービスはデプロイしないことです。Accountsサービスがデプロイされていないため、テストシナリオでは、Accountsサービスが返すことを期待されるイベントを適切なタイミングでポストすることで、Accountsサービスの動作をシミュレートする必要があります。Parasoft SOAtestのテストシナリオは、PortfolioサービスのREST APIを実行するテストと、Accountsサービスからイベントをポストするテストの2つで構成されます。Portfolioサービスがイベントを待機している間にAccountsサービスのイベントがポストされるように、2 つのテストが同時に実行されるように設定する必要があります。
  2. テストを使用してイベントをポストすることでAccountsサービスをシミュレートする代わりに、再利用可能な仮想サービスを構築し、Position Added キューにポストされたイベントを待機して結果のイベントをAccount Updatedキューにポストすると便利です。この仮想マイクロサービスは、複数のテストシナリオで再利用可能です。

1 番目のアプローチはシンプルで、テストインフラストラクチャに外部的に依存しない自己完結型のテストアセットを作ります。2番目のアプローチは再利用可能であり、システムの実際の動作をより忠実にシミュレーションします。しかし、第2のアプローチでは、別個に仮想アセットを構築、デプロイ、管理するコストがあります。

非同期コマンド呼び出しパターンのバリエーションとして、キューの受信イベントを待機し、そのイベントを処理した後、他の1つまたはそれ以上のマイクロサービスが処理する後続イベントを別のキューにパブリッシュするマイクロサービスがあります。

この例では、Invoiceマイクロサービスがテスト対象サービスです。PaymentsサービスはPayment Processed RabbitMQキューにイベントをパブリッシュし、Invoiceサービスがイベントを受け取ります。Invoiceマイクロサービスは、イベントをキューから読み取り、請求書を作成し、イベントをInvoice Createdキューにパブリッシュして、請求書を添付した電子メールを顧客に送信するようにEmailマイクロサービスに指示します。Invoiceマイクロサービスのテストシナリオを作成するには、2つのRabbitMQキューを持ち、Invoiceマイクロサービスをデプロイしたテスト環境を設定します。payment-processedイベントをPayment Processedキューに発行するParasoft SOAtestのテストシナリオを構築することができます。次に、シナリオはInvoice Createdキューをサブスクライブして、Invoiceサービスの応答として適切なinvoice createdイベントがパブリッシュされることを検証します。これは非常に簡単なテストシナリオですが、Invoiceサービスを分離して適切にテストできます。

イベントFirehoseパターン

イベントFirehoseパターンは、複数のソースが非常に多くのイベントを生成し、共通のハブを介して異なるコンシューマーにすばやく配信する必要がある場合に使用されます。このパターンでは、メッセージはトピックを介して交換されます(この点で、メッセージがキューを介して交換される非同期コマンド呼び出しパターンとは異なります)。イベントFirehoseパターンを実装するために使用される一般的なフレームワークは、 Apache Kafka フレームワークであり、おおよそ次のようになります。

Kafkaのトピックをサブスクライブし、受信したイベントを処理し、その結果を第2のKafkaのトピックにパブリッシュするマイクロサービスをテストする場合を考えてみます。たとえば、次のような例です。

この例では、複数のソースからさまざまな気象データを収集するWeather DataトピックをサブスクライブするForecastマイクロサービスがあります。サービスは、データを処理して予報モデルを更新し、Forecast Dataトピックに予報データをパブリッシュします。この場合、Forecastサービスが、特定のWeather Dataイベントセットに対応して、期待されたイベントをForecast Dataトピックにパブリッシュすることを検証する必要があります。

これは、2つのKafkaトピックを持ち、Forecastサービスがデプロイされたテスト環境を設定することで実現できます。テストシナリオでは、まず必要なイベントをWeather Dataトピックにパブリッシュし、Forecast Dataトピックをサブスクライブして、Forecastサービスによって正しい予報データイベントがパブリッシュされたことを検証します。複数のテストシナリオを構築し、Forecastマイクロサービスが処理する可能性があるさまざまなイベントの種類および順序を検証する必要があります。

これは比較的簡単なテストシナリオです。Forecastマイクロサービスが他のマイクロサービスから切り離されているということは、Forecastサービスのテストも他のマイクロサービスから切り離されているという利点があります。この場合、仮想サービスを使用して複雑な環境を設定する必要はありません。単にイベントをパブリッシュし、それに応じて正しいイベントが生成されることを検証するテストシナリオを作成すればよいのです。

テスト環境の設定

多くのマイクロサービス開発チームは、継続的インテグレーション / 継続的デプロイメント(CI / CD)プロセスを採用し、コンテナ化されたマイクロサービスの構築、テスト、デプロイを行ってプロセスを自動化し、更新プログラムのデプロイメントに伴うリスクを低減させています。

このプロセスでは、マイクロサービスを含むコンテナイメージが自動的に作成され、テスト環境(多くの場合、 Kubernetes または OpenShift などのKubernetesベースのディストリビューション によって管理されます)にデプロイされます 。すると、マイクロサービスがエンドツーエンドテスト環境、最終的には運用環境にプッシュされる前に検証を実施できます。 CI/CD for Containerized Microservices および Designing microservices: Continuous integration を読むことをお奨めします。この2つの記事は、この種のプロセスを分かりやすく説明しています。

サービス仮想化を利用したマイクロサービスのテスト

ここで取り上げたテストパターンのいくつかは、依存先マイクロサービスを仮想サービスで代替することを前提としています。これらの仮想サービスは、シミュレート対象のマイクロサービスがコンポーネント化されるのと同じ理由から、高度にコンポーネント化され、容易にデプロイ可能である必要があります。これらの環境でサービス仮想化を機能させるには、簡単にデプロイできるコンテナ化された仮想サービスを作成する必要があります。

コンテナ化された仮想サービスを作成するには、Parasoft Virtualizeとそのすべての依存関係を含む基本イメージを作成し、その上に仮想サービスのすべての仮想アセット設定を含む別のイメージを重ねます。仮想サービス用の新しいイメージは、テスト対象のマイクロサービスおよびすべての(仮想化された)依存先を含むコンテナとともに、Docker / Kubernetes環境にデプロイできます。

まとめ

マイクロサービスを採用する場合、マイクロサービスを適切にテストする方法を理解することが重要です。ここで説明したメッセージングパターンおよび関連するテストパターンは目新しいものではありませんが、マイクロサービスがより一般的になり、マイクロサービスパラダイムを採用するアプリケーションが増えるにつれて、これらのパターンを使用するニーズも大幅に増加しました。

Parasoft SOAtestParasoft Virtualize、およびParasoft Continuous Testing Platformを活用すると、マイクロサービスのテストシナリオを柔軟に作成してデプロイし、マイクロサービスの高い品質と信頼性を確保できます。

(この記事は、開発元Parasoft社 Blog 「How to Approach Testing for Microservices」2018年7月5日の翻訳記事です。)

Parasoft SOAtest/Virtualizeについて

APIのテスト自動化とサービス仮想化を1ツールで

SOAtest/Virtualizeは、APIの開発者/利用者に向けてテストの自動化とテスト環境の仮想化の2つの側面から開発を効率化します。SOAtest/Virtualizeは、APIのテストドライバーを提供し、開発中のAPIのテストを自動化する機能と、APIを利用するアプリケーションが必要とするAPIをスタブとして仮想化する機能を同梱して提供します。

Top