私の新しいプロジェクトでは、TDDを試すことにしました。そして、最初に問題が発生しました。アプリケーションで最初にしたいことは、データソースからデータを読み取る機能を提供することです。この目的のために、私はリポジトリパターンを使用したいと思います。そして今:
私はこれを2日から考えていますが、それでも適切な解決策を見つけることはできません。私は何をすべきですか?
リポジトリが行うことは、ドメインからNHibernateやDoctrineなどのDALフレームワーク、またはSQL実行クラスに変換することです。これは、リポジトリがそのフレームワークのメソッドを呼び出してその職務を実行することを意味します。リポジトリは、データをフェッチするために必要なクエリを構築します。 ORMフレームワークを使用していない場合(そうだといいのですが...)、生のSQLステートメントが構築される場所はリポジトリです。
これらのメソッドの最も基本的なものは保存です。ほとんどの場合、これはオブジェクトをリポジトリから作業ユニット(またはセッション)に渡すだけです。
public void Save(Car car)
{
session.Save(car);
}
しかし、別の例を見てみましょう。たとえば、IDで自動車を取得します。それは次のようになるかもしれません
public function GetCarWithId(String id)
{
return Session.QueryOver<Car>()
.Where(x => x.Id == id)
.SingleOrDefault();
}
まだ複雑すぎませんが、複数の条件で想像できます(「フォルクスワーゲン」グループのすべてのブランドについて2010年以降に製造されたすべての車を入手してください)。これはトリッキーです。したがって、真のTDDの方法では、これをテストする必要があります。これを行うにはいくつかの方法があります。
オプション1:ORMフレームワークへの呼び出しをモックする
もちろん、Sessionオブジェクトをモックして、適切な呼び出しが行われたことを表明するだけです。これはリポジトリをテストしますが、実際にはtest- driven ではありません。これは、リポジトリが内部的に希望どおりに見えることをテストしているだけだからです。テストは基本的に「コードはこのように見えるはずです」と述べています。それでも、これは有効なアプローチですが、この種のテストにはほとんど価値がないように感じます。
オプション2:テストからデータベースを(再)構築
一部のDALフレームワークでは、ドメインをテーブルにマップするために作成したマッピングファイルに基づいて、データベースの完全な構造を構築できます。これらのフレームワークの場合、リポジトリをテストする方法は、テストの最初のステップでインメモリデータベースを使用してデータベースを作成し、DALフレームワークを使用してオブジェクトをインメモリデータベースに追加することです。この後、メモリ内データベースのリポジトリを使用して、メソッドが機能するかどうかをテストできます。これらのテストは低速ですが、非常に有効であり、テストを実行します。 DALフレームワークからの協力が必要です。
オプション3:実際のデータベースでテスト
別のアプローチは、実際のデータベースでテストし、ユニットテストを分離することです。いくつかの方法でこれを行うことができます:テストをトランザクションで囲む、手動でクリーンアップする(保守が非常に難しいことはお勧めしません)、各ステップの後にデータベースを完全に再構築します...構築しているアプリケーションによって、これは場合によっては実現可能ではありません。私のアプリケーションでは、ソース管理からローカル開発データベースを完全に構築でき、リポジトリのユニットテストではトランザクションを使用して、テストを相互に完全に分離します(オープントランザクション、データの挿入、テストリポジトリ、ロールバックトランザクション)。すべてのビルドは、最初にローカル開発データベースをセットアップしてから、そのローカル開発データベースのリポジトリに対してトランザクション分離の単体テストを実行します。純粋なユニットテストより少し遅いですが、テストは非常に価値があり、多くの問題をキャッチします。
DALをテストしない
NHibernateなどのDALフレームワークを使用している場合は、そのフレームワークをテストする必要はありません。ドメインオブジェクトを保存、取得、比較してマッピングファイルをテストし、すべてが問題ないことを確認できます(必ず、あらゆる種類のキャッシュを無効にしてください)が、他の多くのテストと同じように必要ではありません。私はこれを主に、子供に条件がある親のコレクションに対して行う傾向があります。
リポジトリの戻りをテストするときは、ドメインオブジェクトの特定のプロパティが一致するかどうかを確認するだけです。これはIDにすることもできますが、テストでは、人間が読み取れるプロパティを確認する方が多くの場合有益です。 「2010年以降に製造されたすべての車を入手してください...」では、5台の車が返却され、ナンバープレートが「ここにリストを挿入」していることを確認できます。追加の利点は、並べ替えについて考えることを強制し、テストによって自動的に並べ替えを強制することです。多くのアプリケーションが複数回ソートすることに驚かれるでしょう(データベースからソートして戻り、ビューオブジェクトを作成する前にソートしてから、ビューオブジェクトをソートします。すべて同じプロパティで念のため)または、暗黙的にリポジトリのソートを想定し、途中で誤って削除したため、UIが壊れます。
「ユニットテスト」は単なる名前です
私の意見では、単体テストは mostly データベースにヒットしないはずです。ソースからのデータを必要とするすべてのコードがリポジトリを使用してこれを実行し、そのリポジトリが依存関係として挿入されるように、アプリケーションを構築します。これにより、簡単なモックと、必要なすべてのTDDの良さが実現します。しかし、最終的には、リポジトリがその役割を果たしていることを確認し、それを行うための最も簡単な方法がデータベースにヒットする場合は、そうする必要があります。私は長い間、「単体テストはデータベースに触れてはならない」という考えを手放し、これを行うには非常に現実的な理由があることを学びました。ただし、これを自動的かつ繰り返し実行できる場合に限ります。そして、そのようなテストを「単体テスト」または「統合テスト」と呼ぶ天候は悲観的です。
簡単な、または明らかなリポジトリメソッドをテストしないでください。
メソッドが簡単なCRUD操作である場合、実際にテストしているのは、パラメーターが正しくマップされているかどうかだけです。統合テストがある場合、そのようなエラーはとにかくすぐに明らかになります。
これは、次のような簡単なプロパティに適用されるのと同じ原理です。
public property SomeProperty
{
get { return _someProperty; }
set { _someProperty = value; }
}
テストするものがないので、テストしません。検証が必要なプロパティに検証やその他のロジックはありません。
これらのメソッドをテストする場合...
モックはそれを行う方法です。これらは単体テストであることを覚えておいてください。単体テストでデータベースをテストすることはありません。それが統合テストの目的です。
詳細情報
フルスタック、パート3:TDDを使用したリポジトリの構築 (約16分で監視を開始します)。