サービス仮想化をより現実に近づける: ステートフルテストと状態遷移テスト

サービス仮想化において、現実的な仮想サービスを作成する際に重要となる点は、あるテスト実行時の状態を別のテスト実行時にも保持できるよう、サービスの挙動をステートフルにすることです。しかしその限界はどこにあるのでしょうか?シミュレーションはどの時点から過剰なシミュレーションになるのでしょうか?この記事では、状態遷移テストとは何か、またどのような場合に必要となるかを説明します。

機能テストをよりスピーディに実施するには、信頼できる現実的なテスト環境に無制限にアクセスできることが不可欠です。完全なテスト環境には、テスト対象アプリケーション(AUT)とそのすべての依存コンポーネント(API、サードパーティサービス、データベース、アプリケーション、その他のエンドポイントなど)が含まれます。

サービス仮想化は以下を可能にします。

  1. 必要な依存先システムコンポーネントをすべて含む完全なテスト環境にアクセスすること
  2. ステージングテスト環境では不可能な方法で依存先コンポーネントの挙動を変更すること

これにより、早期の段階で、高速で、より完全にテストがおこなえます。

ステートフルテスト

現実的な仮想コンポーネントを作成するうえで重要なのは、サービスの挙動をステートフルにすることです。ステートフルな挙動とは、言い換えれば、仮想化した依存先コンポーネントが、あるテスト実行時の状態を別のテスト実行時にも保持できるということです。例として、ショッピングカートコンポーネントを仮想化してシミュレートするケースを考えてみましょう。カートのアイテムを、検索、追加、取得、削除できるシンプルなAPIがあるとします。挙動がステートレスである場合、アイテムの検索と保存をシミュレートしても、カートの状態は変更されません。カートからアイテムを取得して削除するシナリオテストを実行すると、次の図のように、カートが初期状態(空)のままなので、テストは失敗します。

一方、仮想ショッピングカートの挙動をステートフルにすると、カートの状態(空か、1つ以上のアイテムがあるか)が複数のテストにわたって保持されます。テスト対象アプリケーションからの入力に基づいて状態が変化するので、有効なシナリオテストを何度でも実行できるようになります。

ステートフルテストでは、仮想サービスはカートの状態を空から「追加されたアイテム有り」に変え、適切な情報を返すことができます。カートにアイテムが追加された後でテスト対象アプリケーションがカートを照会すると、適切なデータが返されます。ステートフルテストでは、より現実的なテストが可能になり、下の図のように、カートは追加済みのアイテム情報を返します。

まとめると、仮想サービスを信頼性と再利用性の高いものにするには、仮想サービスが適切にサービスを模倣し、意味のある結果をテスト対象アプリケーションに返せることが重要であり、そのためにステートフルテストが必要になる場合があります。

仮想サービス自体がステートフルであることに加えて、さまざまな入力に応じた状態の変化をシミュレートする必要もあるかもしれません。これを状態遷移テストと呼びます。

状態遷移テストとは

状態遷移は、有限ステートマシン(FSM : finite state machine)のアクションコンポーネントであり、次のように定義されます。

「…任意の時点で有限個の状態のうちただ1つをとることができる抽象機械です。FSMは、外部からの何らかの入力に応答して1つの状態から別の状態に変わることができます。この1つの状態から別の状態への変化を遷移と呼びます。FSMは状態のリスト、初期状態、各遷移の条件によって定義されます。」 – Wikipedia

状態遷移は、オブジェクトを記述するのに便利な方法であり、プログラミングモデルとして明示的に使用されることがよくあります。そうでなくとも、上記のショッピングカートのように、暗黙的な状態遷移によって挙動を記述できる場合もあります。ショッピングカートの例を状態遷移で表すと次のようになります。

カートの初期状態は空であり、カートにアイテムが追加されると、状態がEmpty(空)からItem in cart(カートにアイテム有り)に遷移します。遷移はイベント(この例ではカートのアイテム追加または削除)への応答として開始されます。

多くの場合、遷移が起こるには条件があります。たとえば、Empty状態に戻る遷移は、アイテム数が再びゼロになるときにだけ発生します。また通常、遷移はアクションを実行します。この例では、たとえばカート内のアイテム数の加算がアクションです。カートが明示的に状態遷移としてプログラミングされることはあまりないでしょうが、暗黙的には状態遷移が存在し、カートの挙動を定義するのに利用できます。

もう1つ簡単な例として、特定の回数ログインが失敗するとユーザーをロックアウトするユーザーログインコンポーネントを考えてみましょう。

ログインコンポーネントの初期状態はLogged Out(ログアウト済み)です。ユーザーは入力したPINが正しい場合にだけLogged In(ログイン済み)に遷移できます。定義された回数(MAX_RETRIES)ログインの試行が繰り返されると、アカウントがロックされます。この場合、Account Blocked(アカウントブロック済み)から遷移する方法がないため、Account Blockedが最終状態であるとみなすことができます。

Parasoft Virtualizeで状態をシミュレートする

ところで、上で説明したようなことは、どのようにテストや仮想サービスに役立つのでしょうか?それは、Parasoft Virtualizeを使用してステートフルな挙動をシミュレートし、サービスが複数のテストをまたいでテスト対象アプリケーションが期待する現実的な値を返すようにできるということです。

仮想サービスは、テストからの入力に反応し、必要に応じて値を保存することで、キャプチャされ、シミュレートされるテストデータの有用性を拡大します。ステートフルな挙動によって強化しなければ、データは静的なままです。Parasoft Virtualizeでは、CRUD (Create、Read、Update、Delete)として仮想化されたサービスのテストデータソースを保持し続け、必要に応じてテスト中に操作できます。次の画面ショットを見てください。

Parasoft Virtualizeは、各仮想サービスに関連付けられたテストデータソース(またはエンジン)でステートフルな挙動をサポートします。データソースはテストデータを格納するだけでなく、受信したAPIリクエストに対応してデータを操作するためのCRUDツールも備えています。テストデータソースのデータは、入力イベント(上の状態遷移図で説明したような、入力イベントの受信、状態遷移の発生、アクションの実行など)に基づいて更新されます。

これらを実現するため、Parasoft Virtualizeのテストデータ管理ツールは、データソースと受信イベントに基づいて更新を実行するよう設定できます。

テストデータソースの個々のデータアイテムを更新することも、オブジェクト全体を更新することもできます。また、必要なビジネスルールに基づいてレコードを作成または削除することも可能です。実際、複雑な挙動をシミュレートするのに十分な機能がありますが、そこで問題となるのが、「どこからが過剰なシミュレーションか?」ということです。

ステートフル仮想化のメリットとデメリット

ステートフルではない純粋な仮想化も、実サービスを扱う場合の複雑さを排除し、テスト対象アプリケーションを隔離できるので、テストに非常に役立ちます。さらに、実サービスを排除するということは、本番稼働中のシステムに影響を与えず、サービスをテスト用にコピーする必要もなく、複数のデスクトップで同時に並行してテストを実行できるということを意味します。ただし、純粋な仮想サーバーは「はい、このAPIが呼び出されました」というような確認応答を返すだけです。多くのケースはこれで十分ですが、もちろん、そうでないケースも出てきます。より複雑なユースケースが必要になれば、仮想サーバーも「スマートにする」必要があります。

上記のショッピングカートの例で言えば、仮想ショッピングサービスが、カートにアイテムが追加されたという確認応答を返すだけではなく、実際にカートへのアイテム追加をシミュレートできれば、はるかに有用でしょう。そうなれば、カート内のアイテム数を確認するクエリーは正常終了し、正しい値を返すでしょう。また、上記の状態機械図のcart full(カート満杯)状態をシミュレートすることもできます。テストの結果をより有意義にし、ユースケースのカバレッジを増やすには、ステートフルな挙動が必要です。

しかし、いずれビジネスロジックのシミュレーションが複雑になりすぎるところまで到達します。仮想化の目的は、作業を軽減し、生産性を向上させることですから、複雑なロジックをシミュレートするのはその効果を減少させてしまいます。明確に線を引くのは困難ですが、複雑さがここで示したショッピングカートやログインの例を大きく上回るようであれば、それにかける労力を正当化するのは難しくなるでしょう。どうすれば、正しい挙動をシミュレートしていると確信できますか?テスト対象のアプリケーションが、シミュレートされたサービスとなら正常に動作するが、実サービスでは動作しないという事態は避けたいはずです。検証で実サービスを使用する必要はなくなりません(幸いなことに、ParasoftのEnvironment Managerを使えば、仮想サービスと実サービスを簡単に切り替えられます)。

まとめ

ステートフルな挙動は、現実的な仮想サービスの作成に不可欠であり、テスト対象アプリケーションを隔離するために必要です。テストをより現実的で有効なものにするには、複数のテストにわたってテストデータの状態を保持できる必要があります。Parasoft Virtualizeは、幅広いテストデータ管理機能を備え、API呼び出しの入力に対して、ステートフルなレスポンスや状態に基づいたレスポンスを返すことで、仮想サービスを現実に近づけます。

複雑なビジネスロジックをシミュレートすると利益が減少し、「実物」とかけ離れたものになるリスクがあるため、シミュレーションは慎重に利用するべきです。シミュレーションを賢く利用すると、隔離という点でメリットが大きく、実サーバーへの依存関係を切り離すことにつながります。しかし、最終的な検証では依然として実サーバーの出番があるため、実環境と仮想環境の混合や組み合わせをシームレスにサポートするためにParasoft Virtualizeのようなサービス仮想化ツールが必要です。

この記事は、開発元Parasoft社 Blog 「Adding Life to Service Virtualization: Stateful and State Transition Testing」2019年6月6日の翻訳記事です。)

Parasoft SOAtest/Virtualizeについて

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

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

Top