web-dev-qa-db-ja.com

既知の状態にリセットして、PlayフレームワークのMySQLなどの非メモリデータベースに対して単体テストを作成するにはどうすればよいですか?

Play Framework2.1.0でリレーショナルデータベースを使用するコードをカバーする単体テストを作成したいと思います。これには多くの可能性があり、すべてが問題を引き起こします。

インメモリH2データベースでのテスト

Play Frameworkのドキュメントでは、開発および本番環境で使用されるメインデータベースが他のソフトウェア(MySQLなど)を使用している場合でも、H2インメモリデータベースで単体テストを実行することを提案しています。

_app = Helpers.fakeApplication(Helpers.inMemoryDatabase());
_

私のアプリケーションは、ストアドプロシージャなどの複雑なRDBMS機能を使用せず、ほとんどのデータベースアクセスケースはebean呼び出しであるため、MySQLとH2の両方と互換性があるはずです。

ただし、エボリューションのテーブル作成ステートメントは、_ENGINE = InnoDB_、_DEFAULT CHARACTER SET = utf8_などの指定などのMySQL固有の機能を使用します。_CREATE TABLE_のこれらの独自の部分を削除すると、MySQLはいくつかを使用します。制御できないデフォルト設定であり、バージョンによって異なるため、アプリケーションのメインMySQL構成をテストおよび開発するには、変更する必要があります。

誰かがこのアプローチを使用しました(進化をMySQLとH2の両方と互換性を持たせる)?

それをどのように扱うことができるかの他のアイデア:

  • MySQLとH2の個別の進化(良い考えではありません)
  • H2に_create table_の追加のMySQLのものを無視させるいくつかの方法(MySQL互換モードは機能せず、_default character set_でも文句を言います)。方法がわかりません。

メインデータベースと同じデータベースドライバーでのテスト

H2インメモリデータベースの唯一の利点は、高速であり、開発/本番データベースよりも同じデータベースドライバーでテストする方が、実際の環境に近いため、優れている可能性があります。

Playフレームワークでどのように正しく行うことができますか?

試した:

_Map<String, String> settings = new HashMap<String, String>();
settings.put("db.default.url", "jdbc:mysql://localhost/sometestdatabase");
settings.put("db.default.jndiName", "DefaultDS");
app = Helpers.fakeApplication(settings);
_

ここでは進化が機能しているように見えますが、各テストの前にデータベースをクリーンアップするのが最善の方法は何ですか?各テーブルを切り捨てるカスタムコードを作成することによって?テーブルを削除する場合、次のテストの前に進化が再度実行されますか、それとも_play test_コマンドごとに1回適用されますか?または、Helpers.fakeApplication()呼び出しごとに1回ですか?

ここでのベストプラクティスは何ですか? dbunit について聞いたことがありますが、それほど苦痛や癖なしに統合することは可能ですか?

23
kolen

まず、テストと本番環境で同じRDBMSを使用することをお勧めします。これにより、見つけにくいバグを回避できるからです。

各テストの間にデータベースをクリーンアップする必要性に関しては、Ebean DdlGeneratorを使用してスクリプトを生成し、クリーンなデータベースを作成し、JUnitの@Beforeアノテーションを使用して、すべてのテストの前にこれらのスクリプトを自動的に実行できます。

DdlGeneratorの使用は、次のように実行できます。

    EbeanServer server = Ebean.getServer(serverName);
    ServerConfig config = new ServerConfig();
    DdlGenerator ddl = new DdlGenerator((SpiEbeanServer) server, new MySqlPlatform(), config);

このコードは、テストを継承させることができる基本クラスに配置できます(または、@RunWithアノテーションで使用できるカスタムRunner内に配置できます)。

また、ボイラープレートコードを回避して、FakeApplicationの作成を簡単に自動化することもできます。

役立つリンク:

8
mguillermin

メインデータベースと同じデータベースエンジンを使用し、各テストの前にクリーンアップするために dbunit を使用しました。

public class SomeTest {
    // ...

    @Before
    public void startApp() throws Exception {
        // Set up connection to test database, different from main database. Config better should be used instead of hard-coding.
        Map<String, String> settings = new HashMap<String, String>();
        settings.put("db.default.url", "jdbc:mysql://localhost/somedatabase?characterEncoding=UTF-8&useOldAliasMetadataBehavior=true");
        settings.put("db.default.user", "root");
        settings.put("db.default.password", "root");
        settings.put("db.default.jndiName", "DefaultDS"); // make connection available to dbunit through JNDI
        app = Helpers.fakeApplication(settings);
        Helpers.start(app);

        databaseTester = new JndiDatabaseTester("DefaultDS");

        IDataSet initialDataSet = new FlatXmlDataSetBuilder().build(play.Play.application()
                .resourceAsStream("/resources/dataset.xml"));
        databaseTester.setDataSet(initialDataSet);
        databaseTester.onSetup();
    }

    @After
    public void stopApp() throws Exception {
        databaseTester.onTearDown();
        Helpers.stop(app);
    }
}

私のdataset.xmlには、各テストの前にこれらのテーブルを空にするようにdbunitに指示するテーブル名が含まれています。また、器具を含めることもできます。

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <name_of_my_first_table />
  <name_of_my_second_table />
</dataset>

このアプローチを使用すると、Evolutionはテストデータベースで自動的に実行されるため、テストデータベースからすべてのテーブルを削除すると、それらが再作成されます。

テーブルをクリーンアップするだけでよい場合は、dbunitを使用するのはやり過ぎです。クエリを直接発行するか、ebean DdlGeneratorを使用してテーブルをクリーンアップできます。しかし、私はデータの比較にもdbunitを使用しています。

Runnableが必要でRunnableの実装では例外をスローできないため、Helpers.runningは使用しません。テストには非常に不便です。しかし、running()のコードを見ると、Helpers.start()Helpers.stop()を呼び出すだけなので、これらのメソッドを@Before@Afterで直接呼び出します。

テストの実行にH2を使用しないことを決定しました。はい、実行速度は速くなりますが、MySQLとの違いが大きすぎます。

5
kolen

Postgresデータベースのテストを作成したときは、データベースに接続するためのHashMapを作成し、次にテストクエリを作成して、正しい量のレコードが存在することなどを確認しました...これが私のコードです。

    @Test
public void testDataBase() {
    final HashMap<String,String> postgres = new HashMap<String, String>();
    postgres.put("db.default.driver","org.postgresql.Driver");
    postgres.put("db.default.url","jdbc:postgresql://localhost/myDataBase");
    postgres.put("db.default.user", "postgres");
    postgres.put("db.default.password", "password");

    running(fakeApplication(postgres), new Runnable() {

        @Override
        public void run() {

            //Insert Assertions Here
        }
    });
}
1

誰かがこのアプローチを使用しました(進化をMySQLとH2の両方と互換性を持たせる)?

MySQL固有の機能に対する答えを見つけました: Play 2.xを使用してMySQLデータベースの単体テストを行うにはどうすればよいですか?

1
EECOLOR

に基づいてSlick | JPA | Anormマッピングと関数の両方を検証することが目標である場合は、DBモックを使用することもできます。

適切な場合は、テストDBよりも単体テストに準拠し、管理が容易であるという利点があります(タスクのセットアップ/クリアではなく、同じテストテーブルへのアクセスを回避するためのテストの同期ではありません)。

Anorm自体の仕様で使用されている私のフレームワークAcolyte( http://github.com/cchantep/acolyte )を見ることができます(例: https://github.com /playframework/playframework/blob/master/framework/src/anorm/src/test/scala/anorm/SqlResultSpec.scala )。

1
cchantep