2

Spring でアプリケーションの統合テストをいくつか書いています。いくつかのサービスをテストしたいのですが、それらはいくつかのデータ アクセス オブジェクトを呼び出す可能性があり、新しいデータが DB に保存されます。テスト後にデータベース内のすべてのものをクリアするには、2 つの方法があります。完了:

  1. 一時データベースを使用してそこにテスト データを挿入する
  2. テストを行うたびにすべてのテーブルをクリアする

ただし、手動でクリーニングせずにデータベースを使用できるように、きれいで前向きな方法を探しています。何か案は?

4

5 に答える 5

3

コメントだけでは捉えきれなかったので、質問へのコメントに回答を添えて…

各テストの間にデータベースをセットアップして解体することは、確かにパフォーマンスにとって非常にコストがかかる可能性があります。そして、それをどのように行うかは、データベース自体に完全に依存します. (MS SQL、MySQL、PostgreSQL など) 同じコードを使用しながら、完全にメモリ内のデータベースをスワップ アウトすることができれば、テストごとにセットアップと破棄を行うのが最も高速です。

たとえば、MS SQL のインスタンスに対してテストを実行する必要があるプロジェクトがありました。セットアップとティアダウンにより、テストが大幅に遅くなりましたが、テスト カバレッジを拡大する価値があると判断しました。(統合テストはチェックイン時ではなく夜間に実行したため、大したことではありませんでした。) データベースを完全に更新するために、次のコードを使用しました (C# です。申し訳ありません)。

var procInfo = new ProcessStartInfo
{
    Arguments = string.Format(@"/Action:Publish /SourceFile:{0} /p:CreateNewDatabase=True /TargetConnectionString:""{1}""", ConfigurationManager.AppSettings["SQLPackageFile"], ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString),
    CreateNoWindow = true,
    ErrorDialog = false,
    FileName = ConfigurationManager.AppSettings["SQLPackageExecutable"],
    RedirectStandardOutput = true,
    UseShellExecute = false
};
var proc = new Process
{
    StartInfo = procInfo
};
proc.Start();

次に、テストの構成では、次の 3 つの値がありました。

  1. データベースの接続文字列。
  2. セットアップ/破棄を行うための MS SQL のコマンドライン実行可能ファイル。
  3. 実行可能ファイルがセットアップに使用した SQL パッケージ ファイル。

したがって、各テストの前にこれを実行して、ソース管理からの SQL パッケージに基づいてデータベースを既知の初期状態に完全に更新します。繰り返しますが、これは非常に遅かったですが、うまくいきました。あなたのために働くより速い解決策があるかもしれません。(おそらく、データベースを削除して再作成する代わりに、テーブルを切り捨てて再作成するか、テーブルを削除して再作成するなどです。いじって、ニーズに合ったものを見つけてください。)

さらに、SQL の依存関係以外を汚染することなく、これがドメイン モデルに適合することを確認したかったのです。データ アクセスに使用していたモデルは、リポジトリ パターンでした。基本的に、私たちの中央ドメイン コードには、ドメイン モデル (DB テーブルやその他の依存関係とは結合されていません) と、それらのモデルのリポジトリ インターフェイスがありました。次に、データ アクセス プロジェクトがこれらのインターフェイスを実装し、ドメイン モデルとデータベース テーブル/列の間で内部的にマッピングしました。

(同じインターフェースに対して、他に 2 つのリポジトリ実装プロジェクトがありました。1 つは、ドメイン ユニット テストに使用されるインメモリ実装でした。これは、リポジトリに送信されるモデルの静的リストを保持するだけでした。もう 1 つは、XML ファイルごとに使用する実装でした。 SQL インスタンスを使用せずにソフトウェアを実行するためのデータの永続性 (アプリケーションの任意のインスタンスで使用されるものは、依存性注入コンテナーの構成設定によって決定されます)。

そこで、 というドメインに別のインターフェイスを追加しましたDataResetter。次に、各データ アクセスの実装 (SQL、インメモリ、XML) がそのインターフェイスを実装しました。SQL は上記のコードと構成値を使用し、インメモリは静的リストをクリアし、XML は XML ファイルを削除しました。

これにより、テストでドメイン機能をテストのセットアップとティアダウンに使用できるようになり、依存性注入コンテナーが使用する実装を決定できるようになりました。そうすれば、テストは特定の実装と結合されませんでした。これに対する追加の利点は、ユニットと統合の目的で同じテストを使用できることでした。それらの違いは、構成設定にすぎません。

(最初に完全なドメインがメモリ内のモック実装で動作することをテストし、次に、1 つの依存関係の実装でドメインを再度テストし、別の依存関係の実装で再度テストするなど。最終的には、同じ一連のテストを毎晩 12 回、12 回実行することになりました。システム全体のさまざまな依存関係の実装. 一度に 1 つの依存関係. これにより、テストの任意の実行で新しい変数が 1 つしかないため、何かが壊れたときに簡単に確認できました。)

于 2013-01-04T13:34:07.353 に答える
2

Springフレームワークを使用している場合は、テストの実行方法に関するSpringのリファレンスをすでに確認しておく必要があると思います。

Springを使用すると、トランザクションを直接操作する必要がないため、非常に簡単になります。テストの先頭に@Transactionlアノテーションを追加するだけで、メリットは何ですか。テストをトランザクション化するのですか?はい!他の回答で前述したように、トランザクションをロールバックできるため、DBに何も残りません。このサンプルコードを見てください:

    @Transactional
    public class FictitiousTransactionalTest {

        @Before
        public void setUpTestDataWithinTransaction() {
            // set up test data within the transaction
        }

        @Test
        @Rollback(true)
        public void modifyDatabaseWithinTransaction() {
            // logic which uses the test data and modifies database state
        }
    }

注意すべき重要な点は、データベースはInnoDBでなければならないということです。したがって、デフォルトでmyISAMであるmySQLを使用している場合は、事前にテーブルを変更することを検討してください。

于 2013-01-04T13:37:12.967 に答える
2

別のオプションがあります。サービスをテストするときに、DAO のモックを使用します。

単体テストは統合テストとは異なります。単体テストは、そのすべての依存関係ではなく、対象のクラスについてのみ行う必要があります。

DAO の単体テストは既にトランザクション方式で行っていると想定しているため、それらが適切に機能することを再度証明する必要はありません。

言及しなかった統合テストの別のオプションは、サービスからのすべてのデータベース対話をトランザクションにすることです。操作を実行し、完了したらロールバックします。

一時データベースは魅力的なオプションです。Hypersonic、Derby、SQLLite などの小さくて軽いものを使用できます。欠点は、スキーマを 2 回更新する必要があることです。1 回はテスト データベースで、もう 1 回は運用インスタンスでです。とにかくこれをしなければならない場合は、洗浄です。

于 2013-01-04T13:02:42.630 に答える
1

テストメソッドにアノテーションを付けるだけ@Transactionalで、Springは自動的にロールバックを実行します。

于 2013-01-04T13:43:37.423 に答える
1

別のオプションがあります: トランザクションのロールバック

トランザクションを開いてコミットする方法によっては、トランザクションをコミットせず、代わりにロールバックを行うことが可能かもしれません。

于 2013-01-04T13:34:17.193 に答える