この質問は、多かれ少なかれプログラミング言語に依存しません。しかし、私は主にJavaに夢中になっているので、ここから例を示します。OOPの場合も考えているので、メソッドをテストしたい場合は、そのメソッドクラスのインスタンスが必要です。
ユニットテストのコアルールは、自律的である必要があるということです。これは、クラスをその依存関係から分離することで実現できます。それを行うにはいくつかの方法があり、 IoC (Javaの世界ではSpring、EJB3、その他のフレームワーク/プラットフォームがあります)を使用して依存関係を注入するかどうかによって異なりますインジェクション機能を提供する)および/またはオブジェクトをモックする場合(Javaの場合は JMock および EasyMock )を使用して、テスト対象のクラスをその依存関係。
異なるクラス*のメソッドのグループをテストする必要があり、それらが適切に統合されていることを確認する必要がある場合は、統合テストと記述します。そして、ここに私の質問があります!
テストに必要なデータベースデータが必要なときにそこにあることをどのように確認していますか?そして、なぜあなたはあなたが選んだ方法を選んだのですか?
モチベーションのある答えを提供してください。それはモチベーションの中に興味深い部分があります。 「これがベストプラクティスです」と言っていることを忘れないでください。 本当の動機ではなく、誰かから読んだり聞いたりしたことを繰り返しているだけです。その場合は、理由がベストプラクティスであることを説明してください。
*技術的に正しくない場合でも、ユニットテストの定義に同じクラスの(同じまたは他の)インスタンスで他のメソッドを呼び出す1つのメソッドを含めています。遠慮なく訂正してください。ただし、副次的な問題として残しておきましょう。
API呼び出しを使用してテストデータを作成することを好みます。
テストの開始時に、空のデータベース(メモリ内または本番環境で使用されるものと同じ)を作成し、インストールスクリプトを実行してデータベースを初期化し、データベースで使用されるテストデータを作成します。テストデータの作成は、たとえば Object Mother パターンで構成できるため、同じデータを多くのテストで再利用できますが、多少の違いはあります。
副作用のない再現可能なテストを行うために、すべてのテストの前にデータベースを既知の状態にする必要があります。したがって、テストが終了したら、テストデータベースを削除するか、トランザクションをロールバックして、前のテストが成功したか失敗したかに関係なく、次のテストが常に同じ方法でテストデータを再作成できるようにする必要があります。
データベースダンプ(または同様のもの)のインポートを回避する理由は、テストデータをデータベーススキーマと結合するためです。データベーススキーマが変更された場合は、テストデータも変更または再作成する必要があり、手作業が必要になる場合があります。
テストデータがコードで指定されている場合は、IDEのリファクタリングツールを利用できます。データベーススキーマに影響する変更を加えると、おそらくAPI呼び出しにも影響するため、APIを使用してコードをリファクタリングする必要があります。ほぼ同じ努力で、テストデータの作成をリファクタリングすることもできます-特にリファクタリングを自動化できる場合(名前の変更、パラメーターの導入など)。ただし、テストがデータベースダンプに依存している場合は、APIを使用するコードのリファクタリングに加えて、データベースダンプを手動でリファクタリングする必要があります。
データベースの統合テストに関連するもう1つのことは、以前のデータベーススキーマからのアップグレードが正しく機能することをテストすることです。そのためには、本 データベースのリファクタリング:進化的データベース設計 またはこの記事を読むことをお勧めします: http://martinfowler.com/articles/evodb.html
統合テストでは、アプリケーションが実際にデータベースと通信できることを確認する必要があるため、実際のデータベースでテストする必要があります。データベースを依存関係として分離するということは、データベースが適切にデプロイされ、スキーマが期待どおりであり、アプリが正しい接続文字列で構成されているかどうかの実際のテストを延期することを意味します。本番環境にデプロイするときに、これらの問題を見つけたくありません。
また、事前に作成されたデータセットと空のデータセットの両方でテストする必要があります。アプリがデフォルトの初期データのみを含む空のデータベースで開始し、データの作成と入力を開始するパスと、ストレス、パフォーマンス、など、テストする特定の条件を対象とする明確に定義されたデータセットの両方をテストする必要があります。など。
また、各状態の前に、データベースが既知の状態にあることを確認してください。統合テスト間に依存関係を持たせたくありません。
あなたの質問は実際には2つの質問のようです。データベースをテストから除外する必要がありますか?データベースはいつ作成しますか?データベースにデータをどのように生成する必要がありますか?
可能であれば、実際のデータベースを使用することをお勧めします。多くの場合、CRUDクラスのクエリ(SQL、HQLなどで記述)は、実際のデータベースに直面したときに驚くべき結果を返す可能性があります。これらの問題は早い段階で洗い流すことをお勧めします。多くの場合、開発者はCRUDの非常に薄い単体テストを作成します。最も良性のケースのみをテストします。テストに実際のデータベースを使用すると、気付いていない可能性のあるあらゆる種類のコーナーケースをテストできます。
そうは言っても、他の問題がある可能性があります。各テストの後、データベースを既知の状態に戻します。私の現在の仕事は、すべてのDROPステートメントを実行し、すべてのテーブルを最初から完全に再作成することによってデータベースを削除することです。これはOracleでは非常に低速ですが、HSQLDBなどのインメモリデータベースを使用すると非常に高速になる可能性があります。 Oracle固有の問題をフラッシュする必要がある場合は、データベースのURLとドライバのプロパティを変更してから、Oracleに対して実行します。この種のデータベースの移植性がない場合、Oracleには、この目的のために特別に使用できるある種のデータベーススナップショット機能もあります。データベース全体を以前の状態にロールバックします。私は他のデータベースが持っていると確信しています。
データベースに含まれるデータの種類に応じて、APIまたはロードアプローチがうまく機能する場合と機能しない場合があります。多くの関係を持つ高度に構造化されたデータがある場合、APIを使用すると、データ間の関係を明示的にすることができます。テストデータセットを作成するときに間違いを犯しにくくなります。他のポスターで述べられているように、リファクタリングツールは、データの構造に対する変更の一部を自動的に処理できます。 APIで生成されたテストデータをシナリオを構成するものと考えると便利なことがよくあります。ユーザー/システムがステップX、Y Zを実行すると、そこからテストが実行されます。これらの状態は、ユーザーが使用するのと同じAPIを呼び出すプログラムを作成できるために実現できます。
大量のデータが必要な場合、データ内の関係がほとんどない場合、またはAPIや標準のリレーショナルメカニズムを使用して表現できないデータの一貫性がある場合は、データの読み込みがはるかに重要になります。私のチームで働いていたある仕事は、大規模なネットワークパケット検査システム用のレポートアプリケーションを作成することでした。当時、データ量はかなり多かった。テストケースの有用なサブセットをトリガーするために、スニファーによって生成されたテストデータが本当に必要でした。このように、あるプロトコルに関する情報間の相関は、別のプロトコルに関する情報と相関します。これをAPIでキャプチャすることは困難でした。
ほとんどのデータベースには、テーブルの区切りテキストファイルをインポートおよびエクスポートするためのツールがあります。しかし、多くの場合、それらのサブセットのみが必要です。データダンプの使用をより複雑にします。私の現在の仕事では、Matlabプログラムによって生成され、データベースに保存される実際のデータのダンプを取得する必要があります。データベースデータのサブセットをダンプし、それを「グラウンドトゥルース」と比較してテストできるツールがあります。私たちの抽出ツールは絶えず変更されているようです。
これらの2つのアプローチが排他的に定義されているのはなぜですか?
既存のデータセット、特に過去に問題を引き起こした特定のデータを使用して、notの実行可能な引数を見つけることができません。
問題を引き起こすと想像できるすべての可能な条件でそのデータをプログラムで拡張するnotの実行可能な引数がわかりませんそして統合テストのための少しのランダムデータ。
現代の アジャイルアプローチ では、ユニットテストは毎回同じテストを実行することが本当に重要な場所です。これは、単体テストがバグの発見ではなく、開発時にアプリの機能を維持し、開発者が必要に応じてリファクタリングできるようにすることを目的としているためです。
一方、統合テストは、予期しないバグを見つけるように設計されています。私の意見では、毎回someの異なるデータを使用して実行することもできます。失敗した場合でも、テストで失敗したデータが保持されていることを確認する必要があります。正式な統合テストでは、バグ修正を除いてアプリケーション自体がフリーズするため、テストを変更して、可能な最大数と種類のバグをテストできることを忘れないでください。統合では、アプリでキッチンシンクを投げることができます。
もちろん、他の人が指摘しているように、これは当然、開発しているアプリケーションの種類や所属している組織の種類などによって異なります。
DBUnit を使用して、データベース内のレコードのスナップショットを取得し、XML形式で保存しました。次に、私の単体テスト(データベースが必要な場合は統合テストと呼びます)は、各テストの開始時にXMLファイルからワイプして復元できます。
これが努力する価値があるかどうかは未定です。 1つの問題は、他のテーブルへの依存関係です。静的参照テーブルはそのままにし、要求されたレコードとともにすべての子テーブルを検出して抽出するためのツールをいくつか構築しました。統合テストデータベースのすべての外部キーを無効にするという誰かの推奨事項を読みました。これにより、データの準備がはるかに簡単になりますが、テストで参照整合性の問題をチェックする必要がなくなります。
もう1つの問題は、データベーススキーマの変更です。スナップショットの作成以降に追加された列のデフォルト値を追加するツールをいくつか作成しました。
明らかに、これらのテストは純粋な単体テストよりもはるかに遅い。
個々のクラスの単体テストを作成するのが非常に難しいレガシーコードをテストしようとしている場合、このアプローチは努力する価値があるかもしれません。
私は通常、SQLスクリプトを使用して、説明するシナリオのデータを入力します。
それは簡単で、非常に簡単に再現できます。
テストする必要があるものに応じて、両方を実行します。
SQLスクリプトまたはDBダンプから静的テストデータをインポートします。このデータは、オブジェクトのロード(逆シリアル化またはオブジェクトマッピング)およびSQLクエリテスト(コードが正しい結果を返すかどうかを知りたい場合)で使用されます。
さらに、私は通常、いくつかのバックボーンデータ(構成、ルックアップテーブルに名前を付ける値など)を持っています。これらもこのステップでロードされます。このロードは(DBを最初から作成するとともに)単一のテストであることに注意してください。
DBを変更するコード(オブジェクト-> DB)がある場合、通常は(メモリ内またはどこかのテストインスタンス内の)生きているDBに対して実行します。これは、コードが機能することを確認するためです。大量の行を作成しないでください。テスト後、トランザクションをロールバックします(テストはグローバル状態を変更してはならないというルールに従います)。
もちろん、ルールには例外があります。
これはおそらくすべての質問に答えるわけではありませんが、私は1つのプロジェクトでDBに対して単体テストを行うことを決定しました。私の場合、DB構造もテストが必要だと感じました。つまり、私のDB設計は、アプリケーションに必要なものを提供しましたか。プロジェクトの後半でDB構造が安定していると感じたら、おそらくこれから離れます。
データを生成するために、DBを「ランダムな」データで埋める外部アプリケーションを作成することにしました。個人名や会社名のジェネレーターなどを作成しました。
外部プログラムでこれを行う理由は次のとおりです。1。テスト変更データによってテストを再実行できました。つまり、テストを複数回実行でき、テストによって行われたデータ変更が有効な変更であることを確認しました。 2.必要に応じて、DBをクリーンアップして、新たなスタートを切ることができます。
このアプローチには失敗点があることに同意しますが、私の場合は、人の生成はビジネスロジックの一部であり、テスト用のデータを生成することは実際にはその部分もテストしていました。