リポジトリパターンを使用したTDD


10

私の新しいプロジェクトでは、TDDを試すことにしました。そして、最初に問題が発生しました。アプリケーションで最初にしたいことは、データソースからデータを読み取る機能を提供することです。この目的のために、私はリポジトリパターンを使用したいと思います。そしていま:

  • テストがリポジトリー・インターフェースの実際の実装を対象としている場合、私はデータベースにアクセスできるクラスをテストしますが、それは避けなければならないことを知っています。
  • テストがレポジトリパターンの実際の実装ではない場合、私はうまくテストします...ただ模擬します。これらの単体テストでテストされる製品コードはありません。

私はこれを2日から考えていますが、それでも適切な解決策を見つけることはできません。私は何をすべきですか?

回答:


11

リポジトリが行うことは、ドメインから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オブジェクトをモックして、適切な呼び出しが行われたことを単純に主張できます。これはリポジトリをテストしますが、リポジトリが内部的に希望どおりに見えることをテストしているだけなので、実際にはテスト駆動ではありません。テストは基本的に「コードはこのように見えるはずです」と述べています。それでも、それは有効なアプローチですが、この種のテストにはほとんど価値がないように感じます。

オプション2:テストからデータベースを(再)構築する

一部のDALフレームワークでは、ドメインをテーブルにマップするために作成したマッピングファイルに基づいて、データベースの完全な構造を構築できます。これらのフレームワークの場合、リポジトリをテストする方法は、テストの最初のステップでメモリ内データベースを使用してデータベースを作成し、DALフレームワークを使用してオブジェクトをメモリ内データベースに追加することです。この後、メモリ内データベースのリポジトリを使用して、メソッドが機能するかどうかをテストできます。これらのテストは低速ですが、非常に有効であり、テストを実行します。DALフレームワークからの協力が必要です。

オプション3:実際のデータベースでテストする

別のアプローチは、実際のデータベースでテストし、ユニットテストを分離することです。いくつかの方法でこれを行うことができます:テストをトランザクションで囲む、手動でクリーンアップする(保守が非常に難しいことはお勧めしません)、各ステップの後にデータベースを完全に再構築します...構築しているアプリケーションに応じて、これは実現可能ではありません。私のアプリケーションでは、ソース管理からローカル開発データベースを完全に構築でき、リポジトリのユニットテストではトランザクションを使用して、テストを相互に完全に分離します(オープントランザクション、データの挿入、テストリポジトリ、ロールバックトランザクション)。すべてのビルドでは、最初にローカル開発データベースをセットアップしてから、そのローカル開発データベース上のリポジトリに対してトランザクション分離の単体テストを実行します。それ'

DALをテストしない

NHibernateなどのDALフレームワークを使用している場合は、そのフレームワークをテストする必要はありません。ドメインオブジェクトを保存、取得、比較してマッピングファイルをテストし、すべてが問題ないことを確認できます(必ず、あらゆる種類のキャッシュを無効にしてください)が、他の多くのテストほど必要ではありません。私はこれを主に、子供たちに条件がある親のコレクションのために行う傾向があります。

リポジトリの戻りをテストするときは、ドメインオブジェクトの特定のプロパティが一致するかどうかを確認するだけです。これはIDにすることもできますが、テストでは人間が読み取り可能なプロパティを確認する方が多くの場合有益です。「2010年以降に製造されたすべての車を入手してください...」では、5台の車が返され、ナンバープレートが「ここにリストを挿入」していることを簡単に確認できます。追加の利点は、ソートについて考えることを強制し、テストで自動的にソートを強制することです。あなたは驚かれると思いますどのように多くのアプリケーションのいずれかの並べ替えを複数回(リターンが同じ敷地内のすべてのソートビューオブジェクトを作成する前にして、ソートビューオブジェクト、データベースから選別念のためか、暗黙的にリポジトリの種類を前提とし、誤って削除します)途中でUIが壊れました。

「単体テスト」は単なる名前です

私の意見では、ユニットテストは必要があり、主にデータベースをヒットしません。ソースからのデータを必要とするすべてのコードがリポジトリを使用してこれを実行し、そのリポジトリが依存関係として挿入されるように、アプリケーションを構築します。これにより、簡単なモックと必要なすべてのTDDの良さが実現します。しかし、最終的には、リポジトリがその役割を果たしていることを確認し、それを行う最も簡単な方法がデータベースにヒットする場合は、そうする必要があります。私は長い間、「単体テストはデータベースに触れてはならない」という考えを手放し、これを行うには非常に現実的な理由があることを学びました。ただし、これを自動的かつ繰り返し実行できる場合のみ。そして、そのようなテストを「単体テスト」または「統合テスト」と呼ぶ天候は悲観的です。


3
単体テストと統合テストの目的は異なります。これらのテストの名前は単に装飾的なものではありません。それらも説明的です。
Robert Harvey

9
  1. ささいな、または明白なリポジトリメソッドをテストしないでください。

    メソッドが簡単なCRUD操作である場合、実際にテストしているのは、パラメーターが正しくマップされているかどうかだけです。統合テストがある場合、そのようなエラーはとにかくすぐに明らかになります。

    これは、次のような簡単なプロパティに適用されるのと同じ原理です。

    public property SomeProperty
    {
        get { return _someProperty; }
        set { _someProperty = value; }
    }
    

    テストするものがないので、テストしません。検証が必要なプロパティに検証やその他のロジックはありません。

  2. それでもこれらのメソッドをテストしたい場合...

    モックはそれを行う方法です。これらは単体テストです。 単体テストではデータベースをテストしません。それが統合テストの目的です。

詳細情報
フルスタック、パート3:TDDを使用してリポジトリを構築する(約16分で視聴を開始)。


3
確かに、私はこれを理解しています。それでも、TDDアプローチの場合、最初にこのコードのテストがない場合、コードを記述しないでください。
Thaven 2014

1
@Thaven-YouTubeに「tddは死んでいるのですか?」というタイトルの一連のビデオがあります。それらを見てください。彼らは多くの興味深い点に取り組んでおり、そのうちの1つは、アプリケーションのすべてのレベルでTDDを適用することが必ずしも最良のアイデアではないという概念です。「失敗するテストなしのコードはない」というのは極端な立場であり、結論の1つです。
ジュール、

2
@ジュール:tl; dwとは何ですか?
Robert Harvey 14

1
@RobertHarvey要約するのは難しいですが、最も重要な点は、TDDを常に守らなければならない宗教として扱うことは間違いであることです。これを使用するという選択はトレードオフの一部であり、(1)いくつかの問題でそれなしでより速く作業できる可能性があること、および(2)必要以上に複雑なソリューションに向かわせる可能性があることを考慮する必要があります。特に自分がモックをたくさん使っている場合。
ジュール、

1
ポイント#1の+1。テストは間違っている可能性がありますが、それは通常、取るに足らないことです。テストよりも正確性が明らかな関数をテストしても意味がありません。100%のコードカバレッジを取得することで、プログラムの可能なすべての実行をテストするのに近い場所に到達するのとは異なります。そのため、テストの労力を費やす場所についても賢明であるかもしれません。
ドヴァル2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.