web-dev-qa-db-ja.com

単体テストとデータベース:実際にデータベースに接続するのはどの時点ですか?

データベースに接続するテストクラスの方法に関する質問への回答があります。 "サービステストクラスを接続する必要があります..." および "ユニットテスト-データベース結合アプリ"

つまり、データベースに接続する必要のあるクラスAがあるとしましょう。 Aに実際に接続させる代わりに、Aに接続に使用できるインターフェースをAに提供します。テストのために、このインターフェースをいくつかのもので実装します-もちろん接続しません。クラスBがAをインスタンス化する場合、「実際の」データベース接続をAに渡す必要があります。ただし、Bはデータベース接続を開きます。つまり、Bをテストするには、接続をBに注入します。ただし、BはクラスCでインスタンス化されます。

では、「データベースからデータをフェッチして、このコードの単体テストを作成しない」とはどの時点で言わなければならないのでしょうか。

言い換えると、あるクラスのコードのどこかで私はmustを呼び出す必要がありますsqlDB.connect()または類似の何か。このクラスをテストするにはどうすればよいですか?

そして、それはGUIまたはファイルシステムを処理する必要があるコードと同じですか?


ユニットテストをしたいのですが。他の種類のテストは私の質問とは関係ありません。私はそれで1つのクラスのみをテストすることを知っています(私はキリアンに同意します)。現在、一部のクラスはDBに接続する必要があります。このクラスをテストして「どうすればいいですか」と質問したい場合、「依存関係注入を使用してください!」しかし、それは問題を別のクラスに移すだけですよね?それで、実際に接続を確立するクラスをどのようにテストするのですか?

おまけの質問:いくつかの答えは、「模擬オブジェクトを使用する!」に要約されます。どういう意味ですか?テスト対象のクラスが依存するクラスを模擬しています。今テスト中のクラスをモックして、実際にモックをテストしますか?

37
TobiMcNamobi

テンプレートメソッドパターン が役立つ場合があります。

データベースへの呼び出しをprotectedメソッドでラップします。このクラスをテストするには、実際のデータベース接続クラスから継承し、保護されたメソッドをオーバーライドする偽のオブジェクトを実際にテストします。

このようにして、データベースへの実際の呼び出しがユニットテストの対象になることはありません。しかし、それはこれらの数行のコードだけです。そしてそれは許容範囲です。

1
TobiMcNamobi

単体テストのポイントは、1つのクラスをテストすることです(実際、通常は1つのメソッドをテストする必要があります)。

これは、クラスAをテストするときに、テストデータベースを挿入することを意味します-自己記述型、または超高速のメモリ内データベースなど、ジョブを実行するものは何でも。

ただし、BのクライアントであるクラスAをテストする場合、通常はAオブジェクト全体を他の何か、おそらくプリミティブな事前にプログラムされた方法で実際に機能する何かでモックします-実際のAオブジェクトを使用せず、データベースを使用せずに(Aがデータベース接続全体を呼び出し元に返す場合を除いて-しかし、それはあまりにも恐ろしいので、考えたくありません)。同様に、CのクライアントであるクラスBの単体テストを作成する場合、Bの役割を担うものをモックし、Aを完全に忘れてしまいます。

そうしないと、単体テストではなく、システムまたは統合テストになります。それらも非常に重要ですが、まったく異なる魚のやかんです。そもそも、通常はセットアップと実行に多くの労力を要し、チェックインの前提条件としてそれらを渡すことを要求することは現実的ではありません。

21
Kilian Foth

データベース接続に対して単体テストを実行することは完全に正常であり、一般的な方法です。システム内のすべてが依存性注入可能なpuristアプローチを作成することは単に不可能です。

ここで重要なのは、一時データベースまたはテスト専用データベースに対してテストし、そのテストデータベースを構築するための起動プロセスを可能な限り軽くすることです。

CakePHPでの単体テストには、fixturesと呼ばれるものがあります。フィクスチャは、ユニットテスト用にその場で作成される一時的なデータベーステーブルです。フィクスチャには、それらを作成するための便利なメソッドがあります。テストデータベース内の本番データベースからスキーマを再作成したり、簡単な表記法を使用してスキーマを定義したりできます。

これを成功させる鍵は、ビジネスデータベースを実装せず、テストするコードの側面のみに焦点を当てることです。データモデルが公開されたドキュメントのみを読み取ることを検証する単体テストがある場合、そのテストのテーブルスキーマには、そのコードで必要なフィールドのみが含まれている必要があります。そのコードをテストするためだけにコンテンツ管理データベース全体を再実装する必要はありません。

その他の参考資料。

http://en.wikipedia.org/wiki/Test_fixture

http://phpunit.de/manual/3.7/en/database.html

http://book.cakephp.org/2.0/en/development/testing.html#fixtures

12
Reactgular

コードベースのどこかに、リモートDBに接続する実際のアクションを実行するコード行があります。このコード行は、10回に9回、言語と環境に固有のランタイムライブラリによって提供される「組み込み」メソッドの呼び出しです。そのため、これは「自分の」コードではないため、テストする必要はありません。単体テストの目的では、このメソッド呼び出しが正しく実行されると信頼できます。単体テストスイートで引き続きテストできること、およびテストすべきことは、この呼び出しに使用されるパラメーターが期待どおりであることを確認するようなものです。たとえば、接続文字列が正しいこと、SQLステートメント、またはストアドプロシージャ名。

これは、単体テストがランタイムの「サンドボックス」を離れて外部状態に依存するべきではないという制限の背後にある目的の1つです。実際には非常に実用的です。単体テストの目的は、コードあなたが書いた(またはTDDで書き込もうとしている)が思ったとおりに動作することを確認することです。データベース操作の実行に使用しているライブラリなど、記述していないコードは、記述していないという非常に単純な理由から、ユニットテストのスコープの一部であってはなりません。

integrationテストスイートでは、これらの制限が緩和されています。ここでcanデータベースに触れるテストを設計し、作成したコードが、使用していないコードとうまく機能することを確認します。ただし、これらの2つのテストスイートは分離したままにする必要があります。ユニットテストスイートは実行速度が速いほど効果的であるため(開発者が作成したコードに関するすべてのアサーションが依然として保持されていることをすばやく確認できます)、ほぼ定義上、統合テストが行​​われます。外部リソースへの追加された依存関係のため、桁違いに遅いです。ビルドボットが完全な統合スイートの実行を数時間ごとに処理できるようにし、外部リソースをロックするテストを実行して、開発者がこれらの同じテストをローカルで実行することでお互いのつま先を踏まないようにします。そして、ビルドが壊れた場合、何ですか?ビルドボットがビルドに失敗しないようにすることは、おそらくあるべきよりもはるかに重要です。


これをどれだけ厳密に遵守できるかは、データベースに接続してデータベースを照会するための正確な戦略に依存します。 ADO.NETのSqlConnectionオブジェクトやSqlStatementオブジェクトなど、「ベアボーン」データアクセスフレームワークを使用する必要がある多くの場合、開発者が開発したメソッド全体は、組み込みメソッドの呼び出しと、データベース接続なので、この状況で実行できる最善の方法は、関数全体をモックし、統合テストスイートを信頼することです。また、特定のコード行をテスト目的で置き換えることができるようにクラスをどのように設計するかにも依存します(TobiによるTemplate Methodパターンの提案など)。これは、一部を実行する「部分的なモック」を許可するので良いパターンです副作用のある他のクラスをオーバーライドしながら、実際のクラスのメソッド)。

データ永続性モデルがデータレイヤー内のコード(トリガー、ストアドプロシージャなど)に依存している場合、自分で作成しているコードを実行する方法は、データレイヤー内に存在するか、データレイヤー内に存在するテストを開発する以外にありません。アプリケーションランタイムとDBMSの間の境界。純粋主義者は、このパターンのためにORMのようなものを支持してこのパターンを避けるべきであると言います。私はそんなに遠くに行くとは思わない。言語統合クエリやその他のコンパイラチェック、ドメイン依存の永続化操作の時代でも、データベースをストアドプロシージャを介して公開される操作のみにロックすることに価値があると思います。もちろん、そのようなストアドプロシージャは自動化を使用して検証する必要がありますテスト。しかし、そのようなテストはnitテストではありません。それらはintegrationテストです。

この区別に問題がある場合は、通常、完全な「コードカバレッジ」または「ユニットテストカバレッジ」が重視されていることに基づいています。コードのすべての行がユニットテストでカバーされていることを確認する必要があります。その顔には高貴な目標がありますが、私は食器洗いを言います。その考え方は、この特定のケースをはるかに超えるアンチパターンに役立ちます。たとえば、実行するがexerciseしないアサーションレステストを作成するなどです。これらのタイプのエンドランは、カバレッジ数のためだけに、最小カバレッジを緩和するよりも有害です。コードベースのすべての行が何らかの自動テストによって実行されることを確認したい場合、それは簡単です。コードカバレッジメトリックを計算するときは、統合テストを含めます。さらに一歩進んで、これらの論争のある「Itino」テスト(「Integration in name only」)を分離し、単体テストスイートとこの統合テストのサブカテゴリ(まだかなり高速に実行されるはずです)の間を分離することもできます。ほぼ完全にカバーします。

4
KeithS

単体テストはデータベースに接続しないでください。定義により、システムの残りの部分から完全に分離して、それぞれ1つのコード単位(メソッド)をテストする必要があります。そうでない場合、それらは単体テストではありません。

意味論はさておき、これが有益である理由は無数にあります。

  • テストは桁違いに速く実行されます
  • フィードバックループが瞬時になる(例として、TDDのフィードバックは1秒未満)
  • ビルド/デプロイシステムのテストを並行して実行できます
  • テストでは、データベースを実行する必要はありません(ビルドがはるかに簡単、または少なくとも高速になります)。

ユニットテストはあなたの仕事をチェックする方法です。それらは、特定のメソッドのすべてのシナリオを概説する必要があります。これは、通常、メソッドを経由するすべての異なるパスを意味します。これは、複式簿記と同様に、作成する仕様です。

あなたが説明しているのは、統合テストという別のタイプの自動テストです。それらも非常に重要ですが、理想的にはそれらの数がはるかに少なくなります。ユニットのグループが互いに適切に統合されていることを確認する必要があります。

それでは、データベースアクセスでどのようにテストするのでしょうか。データアクセスコードはすべて特定のレイヤーに配置する必要があるため、アプリケーションコードは実際のデータベースではなくモック可能サービスとやり取りできます。これらのサービスが、あらゆるタイプのSQLデータベース、インメモリテストデータ、またはリモートWebサービスデータによってサポートされているかどうかは問題になりません。それは彼らの関心事ではありません。

理想的には(これは非常に主観的です)、コードの大部分を単体テストでカバーする必要があります。これにより、各ピースが独立して機能するという確信が得られます。ピースが構築されたら、それらを組み立てる必要があります。例-ユーザーのパスワードをハッシュすると、この正確な出力が得られるはずです。

各コンポーネントがおおよそ5つのクラスで構成されているとしましょう。コンポーネント内のすべての障害点をテストしたいとします。これは、すべてが適切に配線されていることを確認するためのテストが大幅に少なくなることを意味します。例-ユーザー名/パスワードを指定してデータベースからユーザーを見つけることができるかテストします。

最後に、実際のビジネス目標を確実に満たすために、いくつかの受け入れテストが必要です。これらの数はさらに少なくなります。彼らは、アプリケーションが実行されていることを確認し、それが行うように構築されたものを実行します。例-このテストデータがあれば、ログインできるはずです。

これら3つのタイプのテストをピラミッドと考えてください。すべてをサポートするにはたくさんの単体テストが必要で、そこから上に向かって作業します。

2