web-dev-qa-db-ja.com

テスト用にDBをシミュレートする方法(Java)

私はJavaでプログラミングしており、私のアプリケーションはDBを多く使用しています。したがって、DBの使用を簡単にテストできることが重要です。
DBテストとは何ですか?私にとっては、2つの簡単な要件を提供する必要があります。

  1. SQL構文を確認します。
  2. さらに重要なことは、特定の状況に応じて、データが正しく選択/更新/挿入されていることを確認してください。

それでは、必要なのはDBだけです。
しかし、実際には、DBをテストに使用するのに困難はほとんどないため、私は好みません。

  • 「テスト用DBを入手するだけで、どれほど難しいでしょうか?」 -まあ、私の職場では、個人的なテストDBを持つことはかなり不可能です。誰でもアクセスできる「パブリック」DBを使用する必要があります。
  • 「これらのテストは確かに高速ではありません...」-DBテストは通常​​のテストよりも遅い傾向があります。遅いテストを行うのは本当に理想的ではありません。
  • 「このプログラムはどんな場合でも処理できるはずです!」 -DBのすべてのケースをシミュレートすることは、やや面倒であり、不可能にさえなります。いずれの場合も、一定量の挿入/更新クエリを作成する必要がありますが、これは面倒で時間がかかります。
  • 「ちょっと待って、そのテーブルに542行あることをどうやって知るのですか?」 -テストの主な原則の1つは、テスト対象のコードとは異なる方法で機能をテストできるようにすることです。 DBを使用する場合、通常、何かを行う方法が1つあります。したがって、テストはコアコードとまったく同じです。

だから、テストに関してはDBが好きではないことを理解できます(もちろん、私はいくつかの点でこれに到達する必要がありますが、テストを使用してほとんどのバグを見つけた後、残りのテストメソッド)。しかし、私は何を探していますか?

ファイルシステムまたは仮想メモリのみを使用して、DB、模擬DBをシミュレートする方法を探しています。多分Javaツール/パッケージがあり、テストごとにシミュレートされたテーブルと行、SQL検証、コードインターフェースを使用してDBモックを(コードインターフェースを使用して)簡単に構築できます)その状態を監視するため(SQLを使用するのではなく)。

この種のツールに精通していますか?


編集:回答ありがとうございます!私はツールを求めていましたが、あなたは問題に関するいくつかのヒントも提供してくれました:)あなたの申し出をチェックするのに時間がかかるので、あなたの答えが満足のいくものであったかどうかは今のところ言えません。

とにかく、私が探しているもののより良いビューがあります-DBMonitorという名前のクラスを想像してください。その機能の1つはテーブル内の行数を見つけることです。 JUnitを使用してその機能をテストする方法の架空のコードを次に示します。

public class TestDBMonitor extends TestCase {

    @Override
    public void setUp() throws Exception {

       MockConnection connection = new MockConnection();

       this.tableName = "table1";
       MockTable table = new MockTable(tableName);

       String columnName = "column1";
       ColumnType columnType = ColumnType.NUMBER;
       int columnSize = 50;
       MockColumn column = new MockColumn(columnName, columnType, columnSize);
       table.addColumn(column);

       for (int i = 0; i < 20; i++) {
           HashMap<MockColumn, Object> fields = new HashMap<MockColumn, Object>();
           fields.put(column, i);
           table.addRow(fields);
       }

       this.connection = connection;
    }

    @Test
    public void testGatherStatistics() throws Exception {

       DBMonitor monitor = new DBMonitor(connection);
       monitor.gatherStatistics();
       assertEquals(((MockConnection) connection).getNumberOfRows(tableName),
                    monitor.getNumberOfRows(tableName));
    }

    String tableName;
    Connection connection;
}

このコードが私の考えを理解するのに十分明確であることを願っています(構文エラーのためにすみません、私は親愛なるEclipse:Pなしで手動で入力していました)。

ところで、私はORMを部分的に使用していますが、生のSQLクエリは非常に単純であり、プラットフォームごとに異なるものであってはなりません。

62
Eyal Roth

古い質問に対する新しい答え(ただし、物事は少し進んでいます):

テスト用にDBをシミュレートする方法(Java)

あなたはそれをシミュレートしません。リポジトリをモックしてテストしないか、テストで同じdbを使用してSQLをテストします。すべてのインメモリDBは完全な互換性がないため、完全なカバレッジと信頼性が得られません。接続、結果セットなどの深いデータベースオブジェクトをモック/シミュレートしようとすることはありません。まったく価値がなく、開発と保守が悪夢です。

個人的なテストDBを持つことはかなり不可能です。誰でもアクセスできる「パブリック」DBを使用する必要があります

残念ながら、多くの企業がまだそのモデルを使用していますが、今では docker があり、ほぼすべてのデータベースにイメージがあります。商用製品には、テストには重要ではないいくつかの制限(最大数GBのデータなど)があります。また、このローカルデータベースにスキーマと構造を作成する必要があります

「これらのテストは確かに高速ではありません...」-DBテストは通常​​のテストよりも遅い傾向があります。遅いテストを行うのは本当に理想的ではありません。

はい、dbテストは遅くなりますが、それほど遅くはありません。私はいくつかの簡単な 測定 を行い、典型的なテストは5-50msかかりました。時間がかかるのは、アプリケーションの起動です。これを高速化する方法はたくさんあります。

  • 最初のDIフレームワーク(スプリングなど)は、アプリケーションの一部のみを実行する方法を提供します。 dbと非db関連のロジックを適切に分離してアプリケーションを作成する場合、テストでは db部分のみを開始
  • 各データベースには、耐久性が低く、はるかに高速なチューニングオプションが多数あります。テストに最適です。 postgresの例
  • データベース全体をtmpfsに入れることもできます

  • 別の有用な戦略は、テストのグループを作成し、dbテストをデフォルトでオフにしておくことです(ビルドが本当に遅くなる場合)。このように誰かが実際にdbで作業している場合、cmd行に追加のフラグを渡すか、IDE(testngグループとカスタムテストセレクターがこれに最適です)を使用する必要があります)

いずれの場合も、一定量の挿入/更新クエリを作成する必要がありますが、これは面倒で時間がかかります

「時間がかかる」部分については上記で説明しました。迷惑ですか?私は2つの方法を見てきました:

  • すべてのテストケースに対して1つのデータセットを準備します。それを維持し、それについて推論する必要があります。通常、コードから分離されています。キロバイトまたはメガバイトです。一つの画面で見ること、理解すること、推論することは大きい。テスト間の結合を導入します。テストAにより多くの行が必要になると、テストBのcount(*)が失敗するためです。いくつかのテストを削除しても、この1つのテストでのみ使用された行がわからないため、成長するだけです。
  • 各テストでデータを準備します。このように、各テストは完全に独立しており、読みやすく、簡単に推論できます。迷惑ですか?いも、まったくない!新しいテストを非常に迅速に記述でき、将来的に多くの作業を節約できます

そのテーブルに542行あることをどのように確認しますか?」-テストの主な原則の1つは、テスト対象のコードとは異なる方法で機能をテストできることです。

ええと…そうでもない。主な原則は、ソフトウェアが特定の入力に応じて目的の出力を生成するかどうかを確認することです。したがって、dao.insert 542回呼び出してからdao.countが542を返す場合、ソフトウェアが指定どおりに動作することを意味します。必要に応じて、その間にコミット/ドロップキャッシュを呼び出すことができます。もちろん、契約の代わりに実装をテストしたい場合は、daoがdbの状態を変更したかどうかを確認します。ただし、常にsql Bを使用してsql Aをテストします(挿入と選択、シーケンスnext_valと戻り値など)。はい、あなたは常に「誰が私のテストをテストするか」という問題を抱えているでしょう、そして答えは:誰もいないので、それらを単純にしてください!

あなたを助けるかもしれない他のツール:

  1. testcontainers は、実際のデータベースを提供するのに役立ちます。

  2. dbunit -テスト間でデータをクリーンアップするのに役立ちます

    短所:

    • スキーマとデータを作成および維持するには、多くの作業が必要です。特に、プロジェクトが集中的な開発段階にある場合。
    • それは別の抽象化レイヤーなので、突然このツールでサポートされていないdb機能を使用したい場合、テストするのは難しいかもしれません
  3. testegration -完全で、すぐに使用でき、拡張可能なライフサイクルを提供する意図(開示:私はクリエーターです)。

    短所:

    • 小規模プロジェクトのみ無料
    • 非常に若いプロジェクト
  4. flyway または liquibase -db移行ツール。これらは、テストのためにローカルデータベースにスキーマとすべての構造を簡単に作成するのに役立ちます。

16
piotrek

Javaには Java DB が付属しています。

そうは言っても、ORMレイヤーを通過しない限り、実稼働で使用するものとは異なるタイプのDBを使用しないことをお勧めします。そうしないと、SQLが思ったほどクロスプラットフォームにならない可能性があります。

また、チェックアウト DbUnit

39
ykaganovich

この目的で Hypersonic を使用しました。基本的に、JARファイル(純粋なJavaインメモリデータベース)であり、独自のJVMまたは独自のJVMで実行でき、実行中にデータベースがあります。その後、停止します。これまで、データベースを純粋なインメモリデータベースとして使用してきましたが、単体テストの実行時にAntを介して起動および停止するのは非常に簡単です。

10
Eddie

SQLを介したデータベース接続などの統合ポイントをテストする方法については、多くの観点があります。私にとってうまくいった私の個人的なルールは次のとおりです。

1)データベースにアクセスするロジックと機能を一般的なビジネスロジックから分離し、インターフェイスの背後に隠します。理由:システム内のロジックの大部分をテストするには、実際のデータベースの代わりにダミー/スタブを使用するのが最も簡単です。理由2:劇的に速い

2)データベースのテストを、単体テストの本体から分離され、セットアップデータベースで実行する必要がある統合テストとして扱います理由:テストの速度と品質

3)すべての開発者は、独自の個別のデータベースを必要とします。チームメイトからの変更に基づいて構造を更新し、データを導入する自動化された方法が必要になります。ポイント4および5を参照してください。

4) http://www.liquibase.org のようなツールを使用して、データベース構造のアップグレードを管理します。理由:既存の構造を変更し、バージョンを前進させる能力に敏you性を与えます

5) http://dbunit.sourceforge.net/ のようなツールを使用して、データを管理します。特定のテストケースとベースデータのシナリオファイル(xmlまたはXLS)を設定し、1つのテストケースに必要なものだけを明確にします。理由:データを手動で挿入および削除するよりもはるかに優れています理由2:テスターがシナリオを調整する方法を理解しやすい理由3:これを実行するのが速い

6)シナリオデータのようなDBUnitも含む機能テストが必要ですが、これははるかに大きなデータセットであり、システム全体を実行します。これにより、a)単体テストが実行され、したがってロジックが健全であるという知識を組み合わせるステップが完了します。ボトムスタック」

この組み合わせは、これまでのところ、テストと製品の高品質を達成するだけでなく、単体テストの開発速度と変更への敏ility性を維持するのにも役立ちました。

10
Paul Keeble

「テスト用DBを入手するだけで、どれほど難しいでしょうか?」 -まあ、私の職場では、個人的なテストDBを持つことはかなり不可能です。誰でもアクセスできる「パブリック」DBを使用する必要があります。

仕事で文化的な問題を抱えているように思えますが、それはあなたの能力を最大限に発揮し、製品の利益を享受するのを妨げる障壁となっています。あなたはそれについて何かをしたいと思うかもしれません。

一方、データベーススキーマがバージョン管理されている場合は、スキーマからデータベースを作成し、テストデータを入力し、テストを実行し、結果を収集してからデータベースを削除するテストビルドを常に使用できます。テスト期間中のみ存在します。ハードウェアに問題がある場合は、既存のインストールの新しいデータベースにすることができます。これは、私が働いている場所で行うことと似ています。

5
banjollity

職場でOracleを使用している場合、フラッシュバックデータベースの復元ポイント機能を使用して、データベースをテスト前の時間に戻すことができます。これにより、DBに個人的に加えた変更はすべてクリアされます。

見る:

https://docs.Oracle.com/cd/E11882_01/backup.112/e10642/flashdb.htm#BRADV710

Oracle Production/workで使用するテストデータベースが必要な場合は、OracleのXE Express Expressデータベースを検索します。これは個人使用には無料で、データベースのサイズは2GB未満に制限されています。

5
Martlark

最近、JavaDBまたは Derby に切り替えてこれを実装しました。 Derby 10.5.1.1はメモリ内表現を実装するようになったため、非常に高速に実行され、ディスクに移動する必要がありません。 Derby In Memory Primer

Oracle、PostgreSQL、およびDerbyで実行するようにアプリケーションを設計しているため、1つのデータベースが他のプラットフォームではサポートされていない機能をサポートしていることがわかります。

3
Blair Zajac

私のAcolyteフレームワークはそのようなDBモックアップに使用できると思います: https://github.com/cchantep/acolyte .

既存のJava(テスト用)をクエリ/更新処理の接続で実行できます。適切な結果セット、更新カウント、または実行ケースに応じた警告を返します。

1
cchantep

現在、作業中のデータベーステスト環境を作成しています。 realデータベース管理システムとシミュレーションデータを使用する必要があると感じています。シミュレートされたDBMSの問題の1つは、SQLが標準として完全に完全にゲル化することはないため、人工的なテスト環境では運用データベースの方言を忠実にサポートする必要があることです。別の問題は、列値の制約、外部キーの制約、一意の制約を広範囲に使用することであり、人工的なツールではおそらくこれらを実装しないため、ユニットテストは合格しますが、システムテストは最初に実際にヒットしたときに失敗します制約。テストに時間がかかりすぎる場合、これは実装エラーを示しており、クエリを調整します(通常、テストデータセットは実稼働に比べて非常に小さいです)。

各開発者マシンと継続的インテグレーションおよびテストサーバー(Hudsonを使用)に実際のDBMSをインストールしました。あなたの作業ポリシーの制限がわからないが、PostgreSQL、MySQL、Oracle XEをインストールして使用するのはとても簡単だ。これらはすべて開発用に無料です(Oracle XEも含む)ので、それらの使用を禁止する合理的な理由はありません。

重要な問題は、データベースが一貫した状態で常にテストを開始することをどのように保証するかです。テストがすべて読み取り専用であれば、問題ありません。コミットを行わないトランザクションで常に実行されるように変更テストを設計できれば、問題はありません。ただし、通常は、更新の逆転について心配する必要があります。これを行うには、初期状態をファイルにエクスポートし、それをテスト後にインポートし直します(Oracleのexpおよびimpシェルコマンドがこれを行います)。または、チェックポイント/ロールバックを使用できます。しかし、もっとエレガントな方法は dbunit のようなツールを使用することです。

これの主な利点は、修正するのがはるかに簡単で、開発者が問題をデバッグしようとする間に実際のシステムテストがブロックされないという点で、より多くのバグを事前にキャッチできることです。これは、より良いコードをより速く、より少ない労力で作成することを意味します。

1
Jim Ferrans

derby を使用してみてください。簡単でポータブルです。 Hibernateを使用すると、アプリが柔軟になります。ダービーでテストし、好きなものを作成して信頼してください。

1
Artic

私はバンジョールに同意します。分離された開発環境とテスト環境をセットアップすることは、最優先事項です。私が使用したすべてのデータベースシステムは、オープンソースであるか、ローカルワークステーションにインストールできる無料の開発者エディションがあります。これにより、本番と同じデータベース方言に対して開発でき、開発データベースへの完全な管理者アクセスが可能になり、リモートサーバーを使用するよりも高速になります。

1
Nat

あなたは、メモリ内のDBテストのためにHSQLDBができました。メモリ内のデータベースを起動し、その上でテストを実行するのは非常に簡単です。
http://hsqldb.org/

1
Pratik Singhal

jOOQ は、SQL抽象化の提供とは別に、JDBC全体をモックできるSPIなどの小さなツールも組み込まれているツールです。 このこのブログ投稿に記載されているように、2つの方法で機能します

MockDataProvider SPIを実装することにより:

// context contains the SQL string and bind variables, etc.
MockDataProvider provider = context -> {

    // This defines the update counts, result sets, etc.
    // depending on the context above.
    return new MockResult[] { ... }
};

上記の実装では、SQL文字列を「解析」していくつかの述語/テーブル情報などを抽出することにより、プログラムですべてのSQLステートメントをインターセプトし、結果を返すことができます。

よりシンプルな(ただし強力ではない)MockFileDatabaseを使用する

...次のような形式(ステートメント/結果のペアのセット):

select first_name, last_name from actor;
> first_name last_name
> ---------- ---------
> GINA       DEGENERES
> WALTER     TORN     
> MARY       KEITEL   
@ rows: 3

上記のファイルは、次のように読み取って使用できます。

import static Java.lang.System.out;
import Java.sql.*;
import org.jooq.tools.jdbc.*;

public class Mocking {
    public static void main(String[] args) throws Exception {
        MockDataProvider db = new MockFileDatabase(
            Mocking.class.getResourceAsStream("/mocking.txt");

        try (Connection c = new MockConnection(db));
            Statement s = c.createStatement()) {

            out.println("Actors:");
            out.println("-------");
            try (ResultSet rs = s.executeQuery(
                "select first_name, last_name from actor")) {
                while (rs.next())
                    out.println(rs.getString(1) 
                        + " " + rs.getString(2));
            }
        }
    }
}

実際にデータベースに接続せずに、JDBC APIを直接使用していることに注目してください。

注意してください、私はjOOQのベンダーで働いているので、この答えは偏っています。

ある時点で、データベース全体を実装していることに注意してください

上記は単純な場合に機能します。ただし、最終的にはデータベース全体を実装することに注意してください。あなたが欲しい:

  1. SQL構文を確認します。

上記のようにexactバージョンで予測されていなかった各構文は、このようなモック手法によって拒否されるため、上記のようにデータベースをモックすることにより、構文を「検証」できます。

SQLを解析するパーサーを実装し( または、再びjOOQを使用 )、SQLステートメントをより簡単に認識して結果を生成できるものに変換できます。しかし、最終的には、これは単にデータベース全体を実装することを意味します。

  1. さらに重要なことは、特定の状況に応じて、データが正しく選択/更新/挿入されていることを確認してください。

これは物事をさらに難しくします。挿入を実行してから更新する場合、更新は挿入された行に影響する場合と影響しない場合があるため、結果は最初に更新してから挿入することと明らかに異なります。

データベースを「モック」するときにこれをどのように確認しますか?各「モックされた」テーブルの状態を記憶する状態マシンが必要です。つまり、データベースを実装します。

モッキングはここまでしか行かない

piotrek も言及されているように、モックはここまでしか行かないでしょう。いくつかの非常によく知られているクエリのみをインターセプトする必要がある単純な場合に役立ちます。システム全体のデータベースをモックしたい場合は不可能です。その場合、実際のデータベース、理想的には本番で使用しているのと同じ製品を使用してください。

0
Lukas Eder

まず、DBアクセスにORMレイヤーを使用していますか?
そうでない場合:あなたが考えているものは役に立たないでしょう。あなたが発射しているSQLが他の何かを使用しているテストケースのように本番環境であなたのDBで動作するかわからない場合のテストの使用は何ですか。
「はい」の場合:さまざまなオプションを指摘できます。

0
Khangharoth