私はこの質問について悪魔の擁護者を少し演じる必要があります。経験が足りないのでそれをうまく守ることができないからです。これが契約です。ユニットテストと統合テストの違いを概念的に取得します。永続化メソッドとリポジトリに特に焦点を当てると、ユニットテストは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を一緒にテストすることが重要です。重要なのは、最終的にユニットテストと統合テストを行うには余計な作業が多すぎるようです。データベースの呼び出しとコードが機能するかどうかを知りたいのですが、テストに時間がかかることはわかっていますが、テストを実行してテストする必要があるため、両方を実行しても意味がありません。重要なことだけをテストしてください。」
次のようなテキストブックの定義でそれを守ることができます。「それは統合テストであり、ユニットテストとしてコードを個別にテストする必要があります。 、ヤダ、ヤダ...」これは、実践と現実の純粋主義的な説明が失われているケースです。私は時々これに遭遇し、最終的に外部依存関係に依存する単体テストコードの背後にある理由を守ることができない場合、私はそれを主張することができません。
この質問についての助けは大歓迎です、ありがとう!
単体テストと統合テストの目的は異なります。
ユニットテストは、コードの機能を検証します...メソッドを呼び出すと、メソッドから期待どおりの結果が得られます。統合テストは、システムとして組み合わせたときのコードの動作をテストします。単体テストがシステムの動作を評価することや、統合テストを期待することはできません特定のメソッドの特定の出力を確認します。
単体テストは、正しく行われると、統合テストよりも簡単にセットアップできます。統合テストのみに依存している場合、テストは次のようになります。
結論:統合テストを使用して、オブジェクト間の接続が正しく機能していることを確認しますが、ユニットテストfirstを使用して機能要件を確認します。
そうは言っても、特定のリポジトリメソッドのユニットテストは必要ない場合があります。ユニットテストはtrivialメソッドでは実行しないでください。オブジェクトのリクエストをORMで生成されたコードに渡し、結果を返すだけの場合は、ほとんどの場合、ユニットテストを行う必要はありません。統合テストで十分です。
私は実用主義者の側にいます。コードを2回テストしないでください。
リポジトリの統合テストのみを作成します。これらのテストは、インメモリデータベースに対して実行される単純なテストセットアップにのみ依存しています。ユニットテストなどのすべてを提供していると思います。
テストは、テストが中断したときに役立ちます:プロアクティブまたはリアクティブ。
単体テストはプロアクティブであり、継続的な機能検証である場合がありますが、統合テストはステージングされた/偽のデータに対してリアクティブです。
検討中のシステムが計算ロジックよりもデータに近い/依存している場合、統合テストはより重要になります。
たとえば、ETLシステムを構築している場合、最も関連性の高いテストは、データ(ステージング、フェイク、ライブ)に関するものです。同じシステムにはいくつかの単体テストがありますが、検証、フィルタリングなどの周りのみです。
リポジトリパターンに計算ロジックやビジネスロジックを含めることはできません。データベースに非常に近いです。しかし、リポジトリはそれを使用するビジネスロジックにも近いです。したがって、それはバランスを打つことの問題です。
単体テストでは、リポジトリの動作をテストできます。統合テストは、リポジトリが呼び出されたときに何が実際に起こったかをテストできます。
さて、「何が本当に起こったのか」は非常に魅力的であるように見えるかもしれません。しかし、これを一貫して繰り返し実行するには、非常にコストがかかる可能性があります。脆性試験の古典的な定義がここに適用されます。
ほとんどの場合、リポジトリを使用するオブジェクトに対してユニットテストを作成するだけで十分です。このようにして、適切なリポジトリメソッドが呼び出され、適切なMocked結果が返されるかどうかをテストします。
単体テストは、統合テスト(設計上)では不可能なレベルのモジュール性を提供します。システムがリファクタリングまたは再構成されている場合(およびwillが発生した場合)ユニットテストは再利用できますが、統合テストは頻繁に書き直す必要があります。単体テストにあるはずの何かの作業を行おうとする統合テストは、多くの場合、あまりにも多くのことを行っているため、保守が難しくなっています。
さらに、単体テストを含めることには次の利点があります。
ユニットテストと統合テストの両方を使用しているときに、作業を分割する(そしてDRYアプローチを実施する)ことができます。機能の小さなユニットのユニットテストに依存するだけで、そのようなロジックを繰り返さないでくださいはすでに統合テストの単体テストにあります。これにより、多くの場合、作業が減ります(したがって、再作業が減ります)。
テストが必要なものは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
クラスをテストする唯一の方法は、十分に公平な統合テストを使用することですが、これは薄いラッパークラスであり、めったに変更されないため、非常に重要です。
ストアドプロシージャも単体テストする必要があります。
これを非常に単純な考えに絞ることができます:バグを見つけるのに役立つので、単体テストを優先します。一貫性がないと混乱が生じるため、これを一貫して実行してください。
その他の理由は、テストにも適用されるため、OO開発を導くのと同じルールに由来します。たとえば、単一責任の原則などです。
一部の単体テストが実行する価値がないように思われる場合は、おそらくそれは、対象が実際に持つ価値がないことを示しています。あるいは、その機能は対応するものよりも抽象的であり、そのためのテスト(およびそのコード)をより高いレベルに抽象化できます。
何でもそうですが、例外があります。そして、プログラミングはまだいくぶん芸術的な形なので、多くの問題は十分に異なっており、最良のアプローチについてそれぞれを評価する必要があります。