32

この夏、私は基本的な ASP.NET/SQL Server CRUD アプリを開発していましたが、単体テストは要件の 1 つでした。データベースに対してテストしようとしたときに、問題が発生しました。私の理解では、単体テストは次のようにする必要があります。

  • ステートレス
  • 互いに独立
  • 同じ結果で繰り返し可能、つまり永続的な変更がない

これらの要件は、データベース用に開発する場合、互いに矛盾しているように見えます。たとえば、挿入する行がまだそこにないことを確認せずに Insert() をテストすることはできないため、最初に Delete() を呼び出す必要があります。しかし、それらがまだそこにない場合はどうなりますか? 次に、まず Exists() 関数を呼び出す必要があります。

私の最終的な解決策には、非常に大きなセットアップ関数 (ヤッ!) と、最初に実行され、セットアップが問題なく実行されたことを示す空のテスト ケースが含まれていました。これは、テストのステートレス性を維持しながら、テストの独立性を犠牲にしています。

私が見つけた別の解決策は、 Roy Osherove の XtUnit のように、簡単にロールバックできるトランザクションで関数呼び出しをラップすることです。これは機能しますが、別のライブラリ、別の依存関係が含まれており、目前の問題に対する解決策としては少し重すぎるようです。

では、この状況に直面したとき、SO コミュニティは何をしたのでしょうか?


tgmdbm は次のように述べています。

通常、お気に入りの自動単体テスト フレームワークを使用して統合テストを実行します。そのため、混乱する人もいますが、同じルールに従っているわけではありません。多くのクラスの具体的な実装を含めることができます (それらは単体テストされているため)。具象クラスが相互に、およびデータベースとどのように相互作用するかをテストしています。

したがって、これを正しく読んだ場合、データ アクセス層を効果的に単体テストする方法は実際にはありません。または、データ アクセス レイヤーの「単体テスト」には、データベースとの実際のやり取りとは関係なく、クラスによって生成された SQL/コマンドなどのテストが含まれますか?

4

9 に答える 9

26

テーブルが存在し、期待される列を含み、適切な制約があることを表明する以外に、データベースを単体テストする実際の方法はありません。しかし、それは通常、実際に行う価値はありません。

通常、データベースの単体テストは行いません。通常、統合テストにはデータベースを使用します。

通常、お気に入りの自動化された単体テストフレームワークを使用して統合テストを実行します。そのため、混乱する人もいますが、同じルールに従っていません。多くのクラスの具体的な実装を含めることができます(ユニットテストされているため)。具体的なクラスが相互に、およびデータベースとどのように相互作用するかをテストしています。

于 2008-08-22T01:43:32.523 に答える
11

DBunit

このツールを使用して、特定の時点でのデータベースの状態をエクスポートできます。その後、単体テストを行うときに、テストの開始時にデータベースを以前の状態に自動的にロールバックできます。私が働いている場所でよく使用します。

于 2008-08-22T01:44:22.193 に答える
5

単体テストでの外部依存関係に対する通常の解決策は、モックオブジェクトを使用することです。つまり、テスト対象の実際のオブジェクトの動作を模倣するライブラリです。これは必ずしも簡単ではなく、場合によっては工夫が必要ですが、「自分で作成」したくない場合は、.Net用の優れた(フリーウェアの)モックライブラリがいくつかあります。2つはすぐに頭に浮かぶ:

RhinoMocksはかなり評判の良いものです。

NMockは別です。

市販の模擬ライブラリもたくさんあります。優れた単体テストを作成することの一部は、実際にそれらのコードを設計することです。たとえば、意味のあるインターフェイスを使用して、それでも動作するインターフェイスの「偽の」バージョンを実装することで、依存オブジェクトを「モック」できます。テスト目的の予測可能な方法。

データベースモックでは、これは、単体テストで処理するために、構成されたテーブル、行、またはデータセットオブジェクトを返すオブジェクトを使用して独自のDBアクセスレイヤーを「モック」することを意味します。

私が働いている場所では、通常、独自のモックライブラリを最初から作成しますが、それはあなたがそうしなければならないという意味ではありません。

于 2008-08-22T01:46:32.907 に答える
4

そうです、データベースにアクセスするリポジトリとサービスにアクセスするようにコードをリファクタリングする必要があります。そうすれば、テスト対象のオブジェクトがデータベースに接触しないように、これらのオブジェクトをモックまたはスタブできます。これは、データベースの状態を保存し、テストのたびにリセットするよりもはるかに高速です。

モックフレームワークとしてMoqを強くお勧めします。RhinoMocksとNMockを使用しました。Moqはとてもシンプルで、他のフレームワークで抱えていたすべての問題を解決しました。

于 2008-08-22T01:52:49.397 に答える
2

ここでは、まさにこの状況で使用している手法について説明しました。

基本的な考え方は、DALの各メソッドを実行し、結果をアサートし、各テストが完了したら、データベースがクリーンになるようにロールバックすることです(ジャンク/テストデータはありません)。

「素晴らしい」とは思えない唯一の問題は、通常、CRUDテスト全体を実行することです(単体テストの観点からは純粋ではありません)が、この統合テストでは、CRUD+マッピングコードの動作を確認できます。このように、それが壊れた場合、アプリケーションを起動する前にあなたは知っているでしょう(私が速く行こうとしているときに私にたくさんの仕事を節約します)

于 2009-02-04T16:46:37.100 に答える
2

私は同じ質問をして、他の回答者と同じ基本的な結論に達しました: 実際の db 通信レイヤーの単体テストを気にしないでください。データを適切にフォーマットするなど)、何らかのダミー データ ソースとセットアップ テストを使用して、取得されるデータを検証します。

私も、単体テストの必要最小限の定義は、多くの Web 開発活動にはあまり適していないと思います。ただし、このページでは、より「高度な」単体テスト モデルについて説明しており、さまざまな状況で単体テストを適用するためのいくつかのアイデアを刺激するのに役立つ場合があります。

単体テスト パターン

于 2009-02-04T16:36:00.147 に答える
1

スクリプトから生成したデータベースの空白のコピーからテストを実行する必要があります。テストを実行してからデータを分析して、テストの実行後にデータが正確に正しいことを確認できます。次に、データベースは使い捨てなので、データベースを削除するだけです。これはすべて自動化でき、アトミックアクションと見なすことができます。

于 2008-08-22T01:46:36.633 に答える
1

データ レイヤーとデータベースを一緒にテストしても、プロジェクトの後半で驚くようなことはほとんどありません。しかし、データベースに対するテストには問題があります。主な問題は、多くのテストで共有されている状態に対してテストしていることです。あるテストでデータベースに行を挿入すると、次のテストでもその行が表示されます。
必要なのは、データベースに加えた変更をロールバックする方法です。TransactionScope
クラスは 、非常に複雑なトランザクションだけでなく、テスト呼び出し中のコードが独自のローカル トランザクションでコミットするネストされたトランザクションを処理するのに十分スマートです。テストにロールバック機能を簡単に追加できることを示す簡単なコードを次に示します。

    [TestFixture]
    public class TrannsactionScopeTests
    {
        private TransactionScope trans = null;

        [SetUp]
        public void SetUp()
        {
            trans = new TransactionScope(TransactionScopeOption.Required);
        }

        [TearDown]
        public void TearDown()
        {
            trans.Dispose();
        }

        [Test]
        public void TestServicedSameTransaction()
        {
            MySimpleClass c = new MySimpleClass();
            long id = c.InsertCategoryStandard("whatever");
            long id2 = c.InsertCategoryStandard("whatever");
            Console.WriteLine("Got id of " + id);
            Console.WriteLine("Got id of " + id2);
            Assert.AreNotEqual(id, id2);
        }
    }
于 2014-04-25T15:24:00.510 に答える
0

ORMとしてLINQtoSQLを使用している場合は、データベースをオンザフライで生成できます(単体テストに使用されたアカウントから十分なアクセス権がある場合)。http://www.aaron-powell.com/blog.aspx?id=1125を参照してください

于 2008-12-16T11:04:08.693 に答える