ドキュメント

§データベースを使用したテスト

アプリケーション全体(データベースを含む)を起動してデータベースアクセスコードをテストする機能テストをScalaTestまたはspecs2を使用して記述することは可能ですが、アプリケーションの小さな部分をテストするために多くのコンポーネントを起動して実行する複雑さのために、アプリケーション全体を起動することは多くの場合望ましくありません。

Playは、データベースを使用してアプリケーションの他の部分から独立してデータベースアクセスコードをテストできるよう支援するいくつかのユーティリティを提供します。これらのユーティリティは、ScalaTestまたはspecs2で簡単に使用でき、データベーステストを、重量級で低速な機能テストよりも、軽量で高速な単体テストにより近づけることができます。

§データベースの使用

データベースバックエンドでテストするには、次のものが必要です。

libraryDependencies += jdbc % Test

データベースに接続するには、最低限、Databasesコンパニオンオブジェクトを使用して、データベースドライバ名とデータベースのURLが必要です。たとえば、MySQLに接続するには、次のようにします。

import play.api.db.Databases

val database = Databases(
  driver = "com.mysql.jdbc.Driver",
  url = "jdbc:mysql://localhost/test"
)

これにより、`localhost`で実行されているMySQLの`test`データベースのデータベース接続プールが`default`という名前で作成されます。データベースの名前は、Playによって内部的にのみ使用されます(たとえば、進化などの他の機能によって、そのデータベースに関連付けられたリソースを読み込むために使用されます)。

カスタム名パラメータやカスタム設定パラメータを指定することで、ユーザー名、パスワード、Playがサポートするさまざまな接続プール設定項目などの、データベースの他の設定を指定することもできます。

import play.api.db.Databases

val database = Databases(
  driver = "com.mysql.jdbc.Driver",
  url = "jdbc:mysql://localhost/test",
  name = "mydatabase",
  config = Map(
    "username" -> "test",
    "password" -> "secret"
  )
)

データベースの使用後、データベースは通常、開いている接続を保持し、実行中のスレッドも持つ可能性のある接続プールによってバックアップされているため、シャットダウンする必要があります。これは、`shutdown`メソッドを呼び出すことによって行われます。

database.shutdown()

データベースを手動で作成してシャットダウンするのは、各テストまたはスイートの周りに起動/シャットダウンコードを実行するテストフレームワークを使用している場合に役立ちます。それ以外の場合は、Playに接続プールを管理させることをお勧めします。

§Playにデータベースの管理を委任する

Playは、Playによって管理されるデータベース接続プールを使用して実行するコードブロックを指定できる`withDatabase`ヘルパーも提供します。Playは、コードブロックの実行が終了した後、それが正しくシャットダウンされるようにします。

import play.api.db.Databases

Databases.withDatabase(
  driver = "com.mysql.jdbc.Driver",
  url = "jdbc:mysql://localhost/test"
) { database =>
  val connection = database.getConnection()
  // ...
}

Database.applyファクトリメソッドと同様に、withDatabaseも、必要に応じてカスタムnameconfigマップを渡すことができます。

通常、すべてのテストから直接`withDatabase`を使用することは、過剰なボイラープレートコードになります。このボイラープレートをテストで使用するために、独自のヘルパーを作成することをお勧めします。たとえば

import play.api.db.Database
import play.api.db.Databases

def withMyDatabase[T](block: Database => T) = {
  Databases.withDatabase(
    driver = "com.mysql.jdbc.Driver",
    url = "jdbc:mysql://localhost/test",
    name = "mydatabase",
    config = Map(
      "username" -> "test",
      "password" -> "secret"
    )
  )(block)
}

その後、最小限のボイラープレートで各テストで簡単に使用できます。

withMyDatabase { database =>
  val connection = database.getConnection()
  // ...
}

**ヒント:**これを使用してテストデータベースの設定を外部化し、環境変数またはシステムプロパティを使用して、使用するデータベースとその接続方法を設定できます。これにより、開発者が自分の環境を自由に設定できる柔軟性が最大限に高まり、開発とは異なる特定の環境を提供するCIシステムにも対応できます。

§インメモリデータベースの使用

テストを実行するためにデータベースなどのインフラストラクチャのインストールを必要としないことを好む人もいます。Playは、これらの目的のためにH2インメモリデータベースを作成するための簡単なヘルパーを提供します。

import play.api.db.Databases

val database = Databases.inMemory()

インメモリデータベースは、カスタム名、カスタムURL引数、カスタム接続プール設定を指定して設定できます。次の例では、`MODE`引数を指定してH2に`MySQL`をエミュレートさせ、接続プールがすべてのステートメントをログするように設定しています。

import play.api.db.Databases

val database = Databases.inMemory(
  name = "mydatabase",
  urlOptions = Map(
    "MODE" -> "MYSQL"
  ),
  config = Map(
    "logStatements" -> true
  )
)

汎用データベースファクトリと同様に、インメモリデータベース接続プールは必ずシャットダウンしてください。

database.shutdown()

テストフレームワークの前後処理機能を使用していない場合は、Playにインメモリデータベースのライフサイクルを管理させることができます。これは、`withInMemory`を使用して簡単に実現できます。

import play.api.db.Databases

Databases.withInMemory() { database =>
  val connection = database.getConnection()

  // ...
}

withDatabaseと同様に、ボイラープレートコードを減らすために、`withInMemory`呼び出しをラップする独自のメソッドを作成することをお勧めします。

import play.api.db.Database
import play.api.db.Databases

def withMyDatabase[T](block: Database => T) = {
  Databases.withInMemory(
    name = "mydatabase",
    urlOptions = Map(
      "MODE" -> "MYSQL"
    ),
    config = Map(
      "logStatements" -> true
    )
  )(block)
}

§進化の適用

テストを実行する際には、通常、データベースのデータベーススキーマを管理したいと考えています。すでに進化を使用している場合は、開発と本番で使用するのと同じ進化をテストで再利用することが理にかなっていることがよくあります。テスト専用のカスタム進化を作成することもできます。Playは、Playアプリケーション全体を実行することなく、進化を適用して管理するための便利なヘルパーを提供します。

進化を適用するには、Evolutionsコンパニオンオブジェクトの`applyEvolutions`を使用できます。

import play.api.db.evolutions._

Evolutions.applyEvolutions(database)

これにより、`evolutions/<databasename>`ディレクトリのクラスパスから進化が読み込まれ、適用されます。

テストの実行後、データベースを元の状態にリセットしたい場合があります。データベーステーブルをすべて削除するような進化のダウンスクリプトを実装している場合は、`cleanupEvolutions`メソッドを呼び出すだけで済みます。

Evolutions.cleanupEvolutions(database)

§カスタム進化

状況によっては、テストでカスタム進化を実行したい場合があります。カスタム進化は、カスタムEvolutionsReaderを使用して使用できます。最も簡単なものはSimpleEvolutionsReaderであり、データベース名とEvolutionスクリプトのシーケンスの事前設定されたマップを受け取る進化リーダーであり、SimpleEvolutionsReaderコンパニオンオブジェクトの便利なメソッドを使用して構築できます。たとえば

import play.api.db.evolutions._

Evolutions.applyEvolutions(
  database,
  SimpleEvolutionsReader.forDefault(
    Evolution(
      1,
      "create table test (id bigint not null, name varchar(255));",
      "drop table test;"
    )
  )
)

カスタム進化のクリーンアップは、`cleanupEvolutions`メソッドを使用して通常の進化と同じ方法で行われます。

Evolutions.cleanupEvolutions(database)

ただし、ここでカスタム進化リーダーを渡す必要はありません。これは、進化の状態(ダウンスクリプトを含む)がデータベースに格納されているためです。ダウンスクリプトはデータベースの削除に使用されます。

カスタム進化スクリプトをコードに配置することが非現実的な場合があります。この場合、ClassLoaderEvolutionsReaderを使用して、カスタムパスでテストリソースディレクトリに配置できます。たとえば

import play.api.db.evolutions._

Evolutions.applyEvolutions(database, ClassLoaderEvolutionsReader.forPrefix("testdatabase/"))

これにより、開発と本番の場合と同じ構造と形式で、`testdatabase/evolutions/<databasename>/<n>.sql`から進化が読み込まれます。

プロジェクトフォルダの外側に進化スクリプトを保存する場合は、EnvironmentEvolutionsReaderを使用して、ファイルシステム上の絶対パスまたはプロジェクトフォルダから見た相対パスからスクリプトを読み込むことができます。

import play.api.Environment
import play.api.db.evolutions._

// Absolute path
Evolutions.applyEvolutions(
  database,
  new EnvironmentEvolutionsReader(Environment.simple(), "/opt/db_migration")
)

// Relative path (based on your project's root folder)
Evolutions.applyEvolutions(
  database,
  new EnvironmentEvolutionsReader(Environment.simple(), "../db_migration")
)

§Playに進化の管理を委任する

applyEvolutionsメソッドとcleanupEvolutionsメソッドは、テストの前後に進化を実行するテストフレームワークを使用している場合に役立ちます。この軽量のアプローチが望ましい場合は、Playは便利な`withEvolutions`メソッドも提供して管理します。

import play.api.db.evolutions._

Evolutions.withEvolutions(database) {
  val connection = database.getConnection()

  // ...
}

当然のことながら、`withEvolutions`は`withDatabase`または`withInMemory`と組み合わせてボイラープレートコードを削減し、データベースをインスタンス化し、進化を実行する関数を定義できます。

import play.api.db.Database
import play.api.db.Databases
import play.api.db.evolutions._

def withMyDatabase[T](block: Database => T) = {
  Databases.withInMemory(
    urlOptions = Map(
      "MODE" -> "MYSQL"
    ),
    config = Map(
      "logStatements" -> true
    )
  ) { database =>
    Evolutions.withEvolutions(
      database,
      SimpleEvolutionsReader.forDefault(
        Evolution(
          1,
          "create table test (id bigint not null, name varchar(255));",
          "drop table test;"
        )
      )
    ) {
      block(database)
    }
  }
}

テストのカスタムデータベース管理メソッドを定義したので、それを簡単に使用できるようになりました。

withMyDatabase { database =>
  val connection = database.getConnection()
  connection.prepareStatement("insert into test values (10, 'testing')").execute()

  connection
    .prepareStatement("select * from test where id = 10")
    .executeQuery()
    .next() must_== true
}

**次へ:**Webサービスクライアントのテスト


このドキュメントに誤りを見つけましたか?このページのソースコードはこちらにあります。ドキュメントガイドラインをお読みいただいた後、プルリクエストを送信していただければ幸いです。ご質問やアドバイスがありましたら、コミュニティフォーラムでコミュニティとの会話を開始してください。