ドキュメント

§specs2 を使用した関数型テストの作成

Play は、関数型テストを支援する多くのクラスと便利なメソッドを提供します。これらのほとんどは、play.api.test パッケージ、または Helpers オブジェクトにあります。

次のものをインポートすることで、これらのメソッドとクラスを追加できます。

import play.api.test._
import play.api.test.Helpers._

§テスト用の Application インスタンスの作成

Play は、多くの場合、実行中の Application をコンテキストとして必要とします。デフォルトの Guice 依存性注入を使用している場合は、異なる設定、ルート、さらには追加のモジュールで構成できる GuiceApplicationBuilder クラスを使用できます。

val application: Application = GuiceApplicationBuilder().build()

§WithApplication

アプリケーションを例に渡すには、WithApplication を使用します。明示的な Application を渡すことができますが、便宜上、デフォルトのアプリケーション(デフォルトの GuiceApplicationBuilder から作成)が提供されます。

WithApplication は組み込みの Around ブロックであるため、独自のデータポピュレーションを提供するようにオーバーライドできます。

abstract class WithDbData extends WithApplication {
  override def wrap[T: AsResult](t: => T): Result = super.wrap {
    setupData()
    t
  }

  def setupData(): Unit = {
    // setup data
  }
}

"Computer model" should {
  "be retrieved by id" in new WithDbData {
    override def running() = {
      // your test code
    }
  }
  "be retrieved by email" in new WithDbData {
    override def running() = {
      // your test code
    }
  }
}

§WithServer

テスト内で実際の HTTP スタックをテストしたい場合は、WithServer を使用してテストサーバーを起動できます。

"test server logic" in new WithServer(app = applicationWithBrowser, port = testPort) {
  override def running() = {
    // The test payment gateway requires a callback to this server before it returns a result...
    val callbackURL = s"http://$myPublicAddress/callback"

    val ws = app.injector.instanceOf[WSClient]

    // await is from play.api.test.FutureAwaits
    val response =
      await(ws.url(testPaymentGatewayURL).withQueryStringParameters("callbackURL" -> callbackURL).get())

    response.status must equalTo(OK)
  }
}

port 値には、サーバーが実行されているポート番号が含まれています。デフォルトではランダムなポートが割り当てられますが、WithServer にポートを渡すか、システムプロパティ testserver.port を設定することで変更できます。これは、継続的インテグレーションサーバーとの統合に役立ち、各ビルドでポートを動的に予約できます。システムプロパティ testserver.address を使用して、テストサーバーがバインドするアドレスも構成できます。設定されていない場合、Play サーバーのデフォルトの "0.0.0.0" を使用します。

アプリケーションはテストサーバーにも渡すことができます。これは、カスタムルートを設定して WS 呼び出しをテストする場合に役立ちます。

val appWithRoutes = GuiceApplicationBuilder()
  .appRoutes { app =>
    val Action = app.injector.instanceOf[DefaultActionBuilder]
    ({
      case ("GET", "/") =>
        Action {
          Ok("ok")
        }
    })
  }
  .build()

"test WSClient logic" in new WithServer(app = appWithRoutes, port = 3333) {
  override def running() = {
    val ws = app.injector.instanceOf[WSClient]
    await(ws.url("http://localhost:3333").get()).status must equalTo(OK)
  }
}

§WithBrowser

ブラウザを使用してアプリケーションをテストする場合は、Selenium WebDriver を使用できます。Play は WebDriver を自動的に起動し、WithBrowser を使用して FluentLenium が提供する便利な API でラップします。WithServer と同様に、ポート、Application を変更し、使用するWebブラウザを選択することもできます。

def applicationWithBrowser = {
  new GuiceApplicationBuilder()
    .appRoutes { app =>
      val Action = app.injector.instanceOf[DefaultActionBuilder]
      ({
        case ("GET", "/") =>
          Action {
            Ok("""
                 |<html>
                 |<body>
                 |  <div id="title">Hello Guest</div>
                 |  <a href="/login">click me</a>
                 |</body>
                 |</html>
            """.stripMargin).as("text/html")
          }
        case ("GET", "/login") =>
          Action {
            Ok("""
                 |<html>
                 |<body>
                 |  <div id="title">Hello Coco</div>
                 |</body>
                 |</html>
            """.stripMargin).as("text/html")
          }
      })
    }
    .build()
}

"run in a browser" in new WithBrowser(webDriver = WebDriverFactory(HTMLUNIT), app = applicationWithBrowser) {
  override def running() = {
    browser.goTo("/")

    // Check the page
    browser.el("#title").text() must equalTo("Hello Guest")

    browser.el("a").click()

    browser.url must equalTo("login")
    browser.el("#title").text() must equalTo("Hello Coco")
  }
}

§注入

インジェクターを暗黙的な app を介して直接使用する関数型テストが多数あります。

"test" in new WithApplication() {
  override def running() = {
    val executionContext = app.injector.instanceOf[ExecutionContext]
    executionContext must beAnInstanceOf[ExecutionContext]
  }
}

Injecting トレイトを使用すると、これを省略できます。

"test" in new WithApplication() with play.api.test.Injecting {
  override def running() = {
    val executionContext = inject[ExecutionContext]
    executionContext must beAnInstanceOf[ExecutionContext]
  }
}

§PlaySpecification

PlaySpecification は、Play ヘルパーメソッドと競合するデフォルトの specs2 仕様で提供されている一部のミキシンを除外する Specification の拡張です。また、便宜上、Play テストヘルパーとタイプもミックスインします。

class ExamplePlaySpecificationSpec extends PlaySpecification {
  "The specification" should {
    "have access to HeaderNames" in {
      USER_AGENT must be_===("User-Agent")
    }

    "have access to Status" in {
      OK must be_===(200)
    }
  }
}

§ビューテンプレートのテスト

テンプレートは標準的な Scala 関数であるため、テストから実行し、結果を確認できます。

"render index template" in new WithApplication {
  override def running() = {
    val html = views.html.index("Coco")

    contentAsString(html) must contain("Hello Coco")
  }
}

§コントローラーのテスト

FakeRequest を提供することで、任意の Action コードを呼び出すことができます。

"respond to the index Action" in new WithApplication {
  override def running() = {
    val controller = app.injector.instanceOf[scalaguide.tests.controllers.HomeController]
    val result     = controller.index()(FakeRequest())

    status(result) must equalTo(OK)
    contentType(result) must beSome("text/plain")
    contentAsString(result) must contain("Hello Bob")
  }
}

技術的には、コントローラーを直接インスタンス化できるため、ここでは WithApplication は必要ありません。ただし、コントローラーの直接インスタンス化は、関数型テストよりもコントローラーの単体テストに近いです。

§ルーターのテスト

自分で Action を呼び出す代わりに、Router に実行させることができます。

"respond to the index Action" in new WithApplication(applicationWithRouter) {
  override def running() = {
val Some(result) = route(app, FakeRequest(GET, "/Bob"))

    status(result) must equalTo(OK)
    contentType(result) must beSome("text/html")
    charset(result) must beSome("utf-8")
    contentAsString(result) must contain("Hello Bob")
  }
}

§モデルのテスト

SQL データベースを使用している場合は、inMemoryDatabase を使用してデータベース接続を H2 データベースのインメモリインスタンスに置き換えることができます。

def appWithMemoryDatabase = new GuiceApplicationBuilder().configure(inMemoryDatabase("test")).build()
"run an application" in new WithApplication(appWithMemoryDatabase) {
  override def running() = {
    val Some(macintosh) = Computer.findById(21)

    macintosh.name must equalTo("Macintosh")
    macintosh.introduced must beSome[String].which(_ must beEqualTo("1984-01-24"))
  }
}

§Messages API のテスト

設定を含む関数型テストの場合、最適な方法は WithApplication を使用して、注入された MessagesApi を取得することです。

"messages" should {
  import play.api.i18n._

  implicit val lang = Lang("en-US")

  "provide default messages with the Java API" in new WithApplication() with Injecting {
    override def running() = {
      val javaMessagesApi = inject[play.i18n.MessagesApi]
      val msg             = javaMessagesApi.get(new play.i18n.Lang(lang), "constraint.email")
      msg must ===("Email")
    }
  }

  "provide default messages with the Scala API" in new WithApplication() with Injecting {
    override def running() = {
      val messagesApi = inject[MessagesApi]
      val msg         = messagesApi("constraint.email")
      msg must ===("Email")
    }
  }
}

設定をカスタマイズする必要がある場合は、DefaultMessagesApiProvider を直接使用するかわりに、GuiceApplicationBuilder に設定値を追加する方が適切です。

次へ: Guice を使用したテスト


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