web-dev-qa-db-ja.com

統合テスト:サービス間のテスト

サービス間通信のテスト戦略に関するアドバイスを探しています。

別のサービス(B)を呼び出す1つのサービス(サービスA)があります。これはREST APIです。どちらのサービスも私が所有しています。

サービス呼び出しに関するいくつかの単体テストを行っており、HTTPライブラリをモックするだけなので、実際にはサービスにリクエストが送信されません。これは単体テストではうまく機能しますが、サービスの呼び出しと応答を実際にテストする統合テストをいくつか追加する価値があるかと思いました。

私が目にする問題は、サービスBがデータベースを更新するため、サービスAの統合テストでは、DBを直接呼び出して、加えた変更をリセットする必要があることです。現在、サービスAはサービスBの実装について必要以上に理解しているため、これは理想的ではありません。

これらのテストは価値がありますか?この種のテストを以前に見たとき、それらはしばしば壊れやすく、開発環境が良好な状態にあることに依存しています。たとえば、これがサードパーティのAPIである場合、それを直接呼び出すテストはありません。

2つのオプションを考えることができます。

  1. サービスAに統合テストを記述し、これらのテストでサービスBのデータベースを呼び出して、必要に応じてデータをリセットまたは挿入します。

  2. モックを使い続け、サービスAに統合テストを追加しないでください。代わりに、サービスBにいくつかの機能テストを追加して、さまざまな残りのエンドポイントをテストします。

何かアドバイスや考えはありますか?

4
M.M

私の経験では、 tests は、実行が制御できない依存関係に拘束されるべきではありません。

まず、テストの範囲を絞り込みましょう。質問で述べたように、テスト中のサービスは [〜#〜] a [〜#〜] なので、テストに焦点を当てましょう [〜#〜] a [〜#〜] [〜#〜] b [〜#〜] の所有権とその状態(動作中、実行中、バグあり、高負荷時)に関係なく、など)。

テストで達成すべき重要なことの1つは決定論です [〜#〜] a [〜#〜] の正しい動作を保証する必要がある唯一の方法は、(非)機能要件の前提に従って)テストを実装することですこれにより、ケースを再現して満足させることができます。瞬間や環境に関係なく、何度も繰り返します。

決定論を達成する方法は、 test doubles を実装することです。モック、スタブ、ダミー、スパイ...したがって、カバーする必要のあるシナリオを(プログラミングまたは構成によって)再現できます。

これは統合テストではないと主張する人もいます。私にとって、統合には、あらゆる種類の性質が一緒に機能するいくつかのコンポーネントが含まれる場合があります。一部は私たちの管理下にあり、他はそうではありません。すべての統合を分離できるので、いくつかの異なる統合条件下でのコンポーネントの動作を確認できます。市場投入までの時間も遅くなるため、テストの速度を低下させる依存関係を分離できる場合は、特に優れています。

私が目にする問題は、サービスBがデータベースを更新するため、サービスAの統合テストでは、DBを直接呼び出して、変更内容をリセットする必要があることです。

[〜#〜] b [〜#〜] DBへの書き込みは、ほとんど逸話的です。問題は実際のサービスB をテストしています。これは、 [〜#〜] b [〜#〜] コードを-間接的にテストしているためです。 [〜#〜] b [〜#〜] が実行されている環境!

本当の危険は、テストの瞬間に [〜#〜] b [〜#〜] が生きている未知の条件にあります。これらの条件は、最悪の場合、テストに合格しない可能性があります。失敗した場合、コードに関係のない問題が原因で失敗します。これらの失敗は、テストされているコードの状態に関する意味のあるフィードバックを提供しません。

コメントされているように、DBへの書き込みは奇妙であり、問​​題が発生する可能性のある事柄は他にもたくさんあります。

  • サービスBにはテスト環境がありません。
  • サービスBにはテスト環境がありますが、重大な変更を含む新しいバージョンがデプロイされています。
  • サービスBはバギーです。
  • サービスBのデータストレージが停止しているか、一時的に利用できません。
  • サービスBは破損したデータで応答します。
  • サービスBはテスト中であり、データは頻繁に変更されます。
  • サービスBは使用できなくなりました。

なぜ非決定論的テストが危険なのか不思議に思っているはずです。テストにおける非決定性の根絶 に関するFowlerのブログを読んで、以下をチェックすることをお勧めします 質問 も。ドクの答えは主題を非常によく要約します。

非決定的テストの例は、 Flaky tests です。不安定なテストは、不確定な状況が原因で失敗するテストです。これらのテストは時々失敗し、その理由はわかりません。問題を再現できません。

不安定なテストを含むテストスイートは、Diana Vaughanが呼ぶ逸脱の正規化の犠牲になる可能性があります。正常で問題ないものとして。

-Building Microservices- by Sam Newman

逸脱の正規化は、悪の種です。

何かアドバイスや考えはありますか?

統合をテストする場合、外部サービスのデータも動作も心配する必要はありません。少なくともまだです。 1

心配する必要があるのは、インターフェース(API)の正しい消費とフィードバックの適切な処理(エラー処理、非直列化、マッピングなど)をテストすることです。つまり、コントラクトです。

最近、私は Test Doubles および Consumer Driven Contracts test の概念を使用して作業を開始し、非常に肯定的な結果が得られました。

これらのテストを構築および維持するために取り組む追加の努力が必要であることは事実です。それが私たちのケースです。ただし、構築、テスト、およびデプロイメントの時間を大幅に削減し、CIからより速く、より意味のあるフィードバックを得ることができます。

上記の記述と@Justinの回答に沿って、 Mountebank のようなツールに興味があるかもしれません。


1:外部サービスの実際の動作を検証するために対処するテストの場所があります。それらは建物のパイプラインの外に配置できます。これらは、グリーン展開に不可欠な場合とそうでない場合があります。それは、サービスによって発生した問題を回避できるかどうかによって異なります。技術的な問題というよりは、ほとんど政治的な問題です。

6
Laiv

そうです、データベース層に直接アクセスするテストはより脆弱になりますが、テストが提供する値によっては、それでも価値があるかもしれません。例えばレガシーアプリで重要で壊れやすい機能をテストする場合、このテストによって提供される値は、この脆弱なテストを維持するコストに見合う価値があります。

そうは言っても、便利だと思う代替アプローチがいくつかあります。

  1. APIを介して変更をリセットできるように、または変更をリセットする必要がないように、サービス/テストをセットアップします。たとえば、テストでユーザーを作成する場合は、ユーザーを削除または非アクティブ化できるエンドポイントを公開するか、テスト実行ごとに一意の接頭辞を持つテストユーザーを作成し、異なる接頭辞を持つユーザーを無視します。テスト容易性はソフトウェアの(非常に価値のある)機能であることを忘れないでください。ソフトウェアのテスト容易性を向上させるためだけに機能を導入することは、何らかの議論の余地があるとは思わないでください。

  2. 偽のHTTPサーバーに対してテストします。受信した要求を記録し、実行中のテストに基づいて適切な応答を送信するモック実装。これには、「実際の」サービスとの相互作用をテストしないという欠点がありますが、単体テストではカバーできない範囲が提供されます。実際、この種のテストでは、「実際の」サービスに対してより困難なシナリオをカバーできます。エラー応答のテスト、または高遅延。

0
Justin

統合テストでは、DBを直接使用しないでください。さて、問題はあなたがテストしたい相互作用です:

+-----+      +-----+      +------+
|  A  |<---->|  B  |<---->|  DB  |
+-----+      +-----+      +------+

A–B相互作用またはA–B–DB相互作用をテストしようとしていますか? A–Bサブシステムのみをテストする必要があり、Bサービスがデータベースに対するある種の抽象化であり、他のサービスがDBへの書き込みを想定していない場合、テストのためにDBにアクセスしないでください。

最も重要な問題は、テーブルの名前の変更、列の追加、DBテクノロジーの変更など、A-B統合テストを更新しないと、Bがデータベースを自由に変更できないことです。

A–Bを分離してテストする最も簡単な方法は、テスト専用に個別のAおよびBインスタンスを起動することです。 DBは事実上Bの一部なので、新しいデータベースから始めることもできます。理想的には、テストごとに新しいデータベースを作成することもできます。例えば。新しいSQLiteデータベースの作成は非常に簡単です。 DBの設定がより複雑な場合は、各テストの前にリセットされる統合テストのためにDBを保持できます。

統合テストは単体テストとは異なります。単体テストでは、すべてのテストを分離して実行する必要があります。多くの場合、環境の初期化は非常に複雑で時間がかかるため、これは統合テストには適していません。多くの場合、個々のテストケースをテストスイートに編成するのが最善です。各テストケースは、前のテストの結果に依存します。環境は、各テストスイートの開始時にのみ初期化されます。より高速なテストと引き換えに、あまり役に立たないテスト結果で支払います。テストスイートの初期テストケースが失敗すると、残りのテストを実行できません。

統合テストを既存のシステムに追加する簡単な方法の1つは、実際の相互作用を記録し、テストのために再生することです。例えば。システムのログ出力を解析できるツールを書いたことがあります。新しいテストケースを作成するには、ログファイルから入力と出力をコピーし、データを匿名化してファイルに保存しますtestname.in.txtおよびtestname.out.txt。次に、テストランナーは、これらのファイルでいっぱいのディレクトリを調べ、入力を再生し、期待される出力と結果を比較します。ただし、代表的なテストケースを選択するように注意する必要があります。同様のテストを繰り返すことは時間の無駄です。

0
amon