統合テストのベストプラクティスから、テストを実行する環境が運用環境(オペレーティングシステムを含む)に可能な限り似ているべきだということも多く聞いたため、テストに使用される多くのインメモリデータベース実装を見ると、本当に混乱しています。 、ライブラリ、データベースエンジンなど
ここで何が欠けていますか?
統合テストのベストプラクティスから、テストを実行する環境が運用環境(オペレーティングシステムを含む)に可能な限り似ているべきだということも多く聞いたため、テストに使用される多くのインメモリデータベース実装を見ると、本当に混乱しています。 、ライブラリ、データベースエンジンなど
ここで何が欠けていますか?
回答:
一般的なソフトウェア開発状況では、テストは2つのポイントで使用されます。開発中と、製品を開発チェーンに沿って移動する前です。
開発中にテストを実行する最初の状況は、短期目標を提供します:タスクを定義する(TDDのように:失敗したテストを記述して合格する)、回帰を防ぎ、変更が他の何かを壊さないようにするなどテストは非常に高速である必要があります。理想的には、テストスイート全体が5秒未満で実行され、コードを作成しながらIDEまたはテキストエディターの隣のループで実行できます。導入されたリグレッションは数秒以内にポップアップします。この段階では、リグレッションとバグを100%キャッチするよりも迅速なテスト実行がより重要であり、本番システムの正確なコピーで開発することは非現実的(または完全に不可能)であるため、ここで完全なテストを達成するために必要な努力は価値がありませんそれ。インメモリデータベースの使用はトレードオフです。これらは本番システムの正確なコピーではなく、ただし、テスト実行を5秒の制限以下に抑えるのに役立ちます。データベース関連のテスト用にわずかに異なるデータベースセットアップを選択するか、テストをまったく行わないかの選択があれば、何を選択するかがわかります。
ただし、開発チェーンに沿ってコードを移動する2番目の状況では、広範なテストが必要です。開発プロセスのこの部分を自動化することができる(そしてする必要がある)ため、はるかに遅いテストを行う余裕があります-完全なテスト実行に数時間かかる場合でも、ナイトリービルドのスケジューリングは、昨日のコードベースの正確な状況を常に把握していることを意味します。生産環境を可能な限り正確にシミュレートすることが重要になりましたが、余裕があります。そのため、メモリ内データベースのトレードオフは行いません。本番システムとまったく同じDBMSのまったく同じバージョンをインストールし、可能であれば、テストを開始する前に実際の本番データを入力します。
環境とのトレードオフの速度/マッチングだと思います。テストは頻繁に実行する必要があり、それはテストが高速になったことを意味します。特に、数秒以上かかることのない単体テスト。
統合テストの実行速度は遅くなりますが、高速の場合はより頻繁に実行できます。たとえば、すべてのコミットの前。もちろん、完全な環境ほど完全ではありませんが、少なくともマッピングレイヤー、生成されたSQL、パーツ間の相互作用などをテストしています。データベースのコストが高い場合は、必ずすべての人のためにライセンスを購入する必要はありません。1日1回または最悪の週に1回テストするコードの100%をカバーするよりも、1時間に1回実行するテストでコードの90%をカバーするより多くのエラーをキャッチできます。
そうは言っても、もちろん実際のデータベースと完全に統合された環境でテストする必要があります。これらのテストをそれほど頻繁に実行することはできませんが、以前のテストではすでに自信が与えられているため、残されているのは奇妙なプラットフォーム固有のバグです。
簡単なテストを行うために、データベースアクセスレイヤーのモックは完全に受け入れられます。を呼び出すとgetName()
、モックされたDAO が呼び出され、ファーストネームに「ジョン」、ラストネームに「スミス」が返され、それらが組み立てられ、すべてが完璧になります。データベースを実際に単体テストする必要はありません。
ロジックがもう少し複雑になると、物事はもう少しなります。メソッド「createOrUpdateUser(...)」がある場合はどうなりますか。データベースのモックを作成した場合、モックがオブジェクトを返さないときに特定のパラメーターで特定のメソッドが1回呼び出され、既存のオブジェクトを返すときに別のメソッドがデータベースで呼び出されることを確認できます。これにより、特別なメモリデータベースを起動し、事前に構成されたデータを使用してそのコードをテストするのが簡単になる(特に既に存在する場合)ファジーラインに到達し始めます。
私が取り組んだ実際のコード(販売時点)には、resumeSuspededTransaction(...)
メソッドがありました。これにより、データベースからオブジェクト(およびそのコンポーネント)にトランザクションがプルされ、データベースが更新されます。私たちはそれをserial笑し、データベースに送られるデータのシリアライゼーションとデシリアライゼーションでコードのどこかにバグが潜んでいました(データベースで異なってシリアライズされたタイプを変更しました)。
モックはバグを表示しませんでした。なぜなら、トランザクションをシリアライズし、モックに保存し、モックからデシリアライズし、それらが等しいことをテストするためです。ただし、データベースの先頭にゼロを付けてオブジェクトをシリアル化すると、オブジェクトは削除され、ゼロのない文字列に再結合されます。トラブルシューティングを通じてデータベースなしでバグを発見しました(そこにあることがわかったら、見つけるのはそれほど難しくありませんでした)。
後で、そこにデータベースを配置し、メモリ内のデータベースに移動した場合、バグはそのjunitテストを通過しなかったことに気付きました。
メモリ内データベースには次の利点があります。
素人の言葉で:
アーキテクチャの重要な部分のモックは、単体テストでは問題ありません(必須)。
しかし、統合テストについては、あなたに強く同意します。モッキングは行われるべきではなく、実際の環境と可能な限り類似した環境が提供されるべきです。
結局のところ、統合テストは、アーキテクチャのさまざまな部分が一緒にどのように動作するかをテストすることです。