web-dev-qa-db-ja.com

リポジトリメソッドをテストするためにユニットテストが必要なのはなぜですか?

私はこの質問について悪魔の擁護者を少し演じる必要があります。経験が足りないのでそれをうまく守ることができないからです。これが契約です。ユニットテストと統合テストの違いを概念的に取得します。永続化メソッドとリポジトリに特に焦点を当てると、ユニットテストはMoqなどのフレームワークを介してモックを使用し、検索された順序が期待どおりに返されたと主張します。

次の単体テストを作成したとします。

[TestMethod]
public void GetOrderByIDTest()
{
   //Uses Moq for dependency for getting order to make sure 
   //ID I set up in 'Arrange' is same one returned to test in 'Assertion'
}

したがって、OrderIdExpected = 5を設定し、モックオブジェクトがテストに合格するIDとして5を返す場合。わかった。私はコードをユニットテストして、コードのプリフォームが期待されるオブジェクトとIDを返し、それ以外のものを返さないことを確認します。

私が得る議論はこれです:

「ユニットテストをスキップして統合テストを実行するのはなぜですか?データベースストアドプロシージャandを一緒にテストすることが重要です。重要なのは、最終的にユニットテストと統合テストを行うには余計な作業が多すぎるようです。データベースの呼び出しとコードが機能するかどうかを知りたいのですが、テストに時間がかかることはわかっていますが、テストを実行してテストする必要があるため、両方を実行しても意味がありません。重要なことだけをテストしてください。」

次のようなテキストブックの定義でそれを守ることができます。「それは統合テストであり、ユニットテストとしてコードを個別にテストする必要があります。 、ヤダ、ヤダ...」これは、実践と現実の純粋主義的な説明が失われているケースです。私は時々これに遭遇し、最終的に外部依存関係に依存する単体テストコードの背後にある理由を守ることができない場合、私はそれを主張することができません。

この質問についての助けは大歓迎です、ありがとう!

19
atconway

単体テストと統合テストの目的は異なります。

ユニットテストは、コードの機能を検証します...メソッドを呼び出すと、メソッドから期待どおりの結果が得られます。統合テストは、システムとして組み合わせたときのコードの動作をテストします単体テストがシステムの動作を評価することや、統合テストを期待することはできません特定のメソッドの特定の出力を確認します。

単体テストは、正しく行われると、統合テストよりも簡単にセットアップできます。統合テストのみに依存している場合、テストは次のようになります。

  1. 全体的に書くのが難しくなる
  2. 必要な依存関係がすべてあるため、より脆弱になります。
  3. コードカバレッジを減らします。

結論:統合テストを使用して、オブジェクト間の接続が正しく機能していることを確認しますが、ユニットテストfirstを使用して機能要件を確認します。


そうは言っても、特定のリポジトリメソッドのユニットテストは必要ない場合があります。ユニットテストはtrivialメソッドでは実行しないでください。オブジェクトのリクエストをORMで生成されたコードに渡し、結果を返すだけの場合は、ほとんどの場合、ユニットテストを行う必要はありません。統合テストで十分です。

20
Robert Harvey

私は実用主義者の側にいます。コードを2回テストしないでください。

リポジトリの統合テストのみを作成します。これらのテストは、インメモリデータベースに対して実行される単純なテストセットアップにのみ依存しています。ユニットテストなどのすべてを提供していると思います。

  1. TDDを行うときに、ユニットテストの代わりに使用できます。実際のコードから始める前に、さらにいくつかのテストコードボイラープレートを書く必要がありますが、すべてが整ったら、red/green/refactorアプローチでうまく機能します。
  2. 彼らはリポジトリの実際のコードをテストします-SQL文字列またはORMコマンドに含まれるコード。実際になんらかの文字列をStatementExecutorに送信したことを確認するよりも、クエリが正しいことを確認する方が重要です。
  3. 回帰テストとして優れています。失敗した場合は、常に、考慮されていないスキーマの変更などの実際の問題が原因です。
  4. データベーススキーマを変更する場合は必須です。テストに合格する限り、アプリケーションを壊すような方法でスキーマを変更していないと確信できます。 (この場合、単体テストは役に立ちません。ストアドプロシージャが存在しなくなっても、単体テストは引き続き成功します。)
12
waxwing

テストは、テストが中断したときに役立ちます:プロアクティブまたはリアクティブ。

単体テストはプロアクティブであり、継続的な機能検証である場合がありますが、統合テストはステージングされた/偽のデータに対してリアクティブです。

検討中のシステムが計算ロジックよりもデータに近い/依存している場合、統合テストはより重要になります。

たとえば、ETLシステムを構築している場合、最も関連性の高いテストは、データ(ステージング、フェイク、ライブ)に関するものです。同じシステムにはいくつかの単体テストがありますが、検証、フィルタリングなどの周りのみです。

リポジトリパターンに計算ロジックやビジネスロジックを含めることはできません。データベースに非常に近いです。しかし、リポジトリはそれを使用するビジネスロジックにも近いです。したがって、それはバランスを打つことの問題です。

単体テストでは、リポジトリの動作をテストできます。統合テストは、リポジトリが呼び出されたときに何が実際に起こったかをテストできます。

さて、「何が本当に起こったのか」は非常に魅力的であるように見えるかもしれません。しかし、これを一貫して繰り返し実行するには、非常にコストがかかる可能性があります。脆性試験の古典的な定義がここに適用されます。

ほとんどの場合、リポジトリを使用するオブジェクトに対してユニットテストを作成するだけで十分です。このようにして、適切なリポジトリメソッドが呼び出され、適切なMocked結果が返されるかどうかをテストします。

4
Otpidus

単体テストは、統合テスト(設計上)では不可能なレベルのモジュール性を提供します。システムがリファクタリングまたは再構成されている場合(およびwillが発生した場合)ユニットテストは再利用できますが、統合テストは頻繁に書き直す必要があります。単体テストにあるはずの何かの作業を行おうとする統合テストは、多くの場合、あまりにも多くのことを行っているため、保守が難しくなっています。

さらに、単体テストを含めることには次の利点があります。

  1. 単体テストを使用すると、失敗した統合テスト(おそらく回帰バグが原因)をすばやく分解し、原因を特定できます。さらに、これにより、図やその他のドキュメントよりも迅速に問題がチーム全体に伝えられます。
  2. 単体テストは、コードとともに、例やドキュメント(実際にコンパイルされるの一種のドキュメント)として機能します。他のドキュメントとは異なり、古くなっているとすぐにわかります。
  3. 単体テストは、より大きなパフォーマンスの問題を分解しようとするときのベースラインパフォーマンスインジケーターとして機能しますが、統合テストは、問題を見つけるために多くの機器を必要とする傾向があります。

ユニットテストと統合テストの両方を使用しているときに、作業を分割する(そしてDRYアプローチを実施する)ことができます。機能の小さなユニットのユニットテストに依存するだけで、そのようなロジックを繰り返さないでくださいはすでに統合テストの単体テストにあります。これにより、多くの場合、作業が減ります(したがって、再作業が減ります)。

4
Zachary Yates

テストが必要なものは3つあります。ストアドプロシージャ、ストアドプロシージャを呼び出すコード(つまり、リポジトリクラス)、およびコンシューマです。リポジトリの仕事は、クエリを生成し、返されたデータセットをドメインオブジェクトに変換することです。クエリの実際の実行やデータセットの作成とは別に、単体テストをサポートするのに十分なコードがあります。

したがって(これは非常に単純化された例です):

interface IOrderRepository
{
    Order GetOrderByID(Guid id);
}

class OrderRepository : IOrderRepository
{
    private readonly ISqlExecutor sqlExecutor;
    public OrderRepository(ISqlExecutor sqlExecutor)
    {
        this.sqlExecutor = sqlExecutor;
    }

    public Order GetOrderByID(Guid id)
    {
        var sql = "SELECT blah, blah FROM Order WHERE OrderId = @p0";
        var dataset = this.sqlExecutor.Execute(sql, p0 = id);
        var result = this.orderFromDataset(dataset);
        return result;
    }
}

次に、OrderRepositoryをテストするときに、モックされたISqlExecutorを渡し、テスト対象のオブジェクトが正しいSQL(つまり、その仕事)を渡し、適切なOrderオブジェクトが返されることを確認します結果データセット(モックも同様)。具象SqlExecutorクラスをテストする唯一の方法は、十分に公平な統合テストを使用することですが、これは薄いラッパークラスであり、めったに変更されないため、非常に重要です。

ストアドプロシージャも単体テストする必要があります。

2
Scott Whitlock

これを非常に単純な考えに絞ることができます:バグを見つけるのに役立つので、単体テストを優先します。一貫性がないと混乱が生じるため、これを一貫して実行してください。

その他の理由は、テストにも適用されるため、OO開発を導くのと同じルールに由来します。たとえば、単一責任の原則などです。

一部の単体テストが実行する価値がないように思われる場合は、おそらくそれは、対象が実際に持つ価値がないことを示しています。あるいは、その機能は対応するものよりも抽象的であり、そのためのテスト(およびそのコード)をより高いレベルに抽象化できます。

何でもそうですが、例外があります。そして、プログラミングはまだいくぶん芸術的な形なので、多くの問題は十分に異なっており、最良のアプローチについてそれぞれを評価する必要があります。

0
CL22