Hatena::ブログ(Diary)

mumoshu.log

2010-12-12

[][]Play+ScalaによるGAEアプリの開発方法について 00:26

(この記事は Scala Advent Calendar jp 2010 の6日目です。)

Play! + Scala + GAEのWebアプリ開発はどんなものか調べてみよう…とおもったら、

既に@ymnkさんがplay+scala+gaeのサンプルアプリGitHub - ymnk/lists-with-play-scala-gae: A sample program for Play! Scala with GAEを作られていました。

このサンプルは、

を使って作られていて、GAEデプロイすればそのまま動きます!

これから始める方は、このサンプルをベースに始めると良いと思います。

素晴らしいサンプルアプリをありがとうございます!>@ymnkさん


と、これだけではアレなので、今から始める方向けにplay+scala+gaeによる開発環境の構築からGAEへのデプロイまでの手順を、またガッツり実アプリを組む方向けにFunctionalTestの書き方を残しておきます。

環境構築〜サンプルアプリの実行とGAEへのデプロイ手順

play run

http://localhost:9000/にアクセスすると、appengineのdev_server上でPlay!アプリが動いています。

play gae:deploy

http://アプリID.appspot.comにアクセスすると、Play!アプリGAEアプリ上で動いています(!)

FunctionalTest

Play!のView & Controller、SienaのModel、GAEの開発環境(認証とDataStore)を通したファンクションテストを書いてみます。

ポイントは、

  • GAEのテスト用DataStoreとユーザ認証のセットアップ。これにより、テストケース上で、DataStoreにアクセスしたり、googleのユーザ認証を前提としたコードをテストできます。
  • Siena用のFixtureクラスがないので、テストのsetUp時にテストデータをテスト用DataStoreにinsert
  • FunSuite + ShouldMatchers + Play!提供のテストメソッドの組み合わせ。見慣れたscalatestでPlay!アプリのテストが書けます。

です。

コード

import com.google.appengine.tools.development.testing.{LocalDatastoreServiceTestConfig, LocalUserServiceTestConfig, LocalServiceTestHelper}
import org.junit._
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.{FunSuite, BeforeAndAfterEach}
import play.test._
import play.mvc._
import play.mvc.Http._
import models._

import collection.JavaConversions._

class ListsTest extends FunctionalTest with FunSuite with ShouldMatchers with BeforeAndAfterEach with Browser {
  /**
   * this emulates user login
   * see: http://code.google.com/intl/ja/appengine/docs/java/tools/localunittesting.html#Writing_Authentication_Tests
   */
  private val helper: LocalServiceTestHelper =
          new LocalServiceTestHelper(new LocalUserServiceTestConfig(), new LocalDatastoreServiceTestConfig())
              .setEnvIsAdmin(true).setEnvEmail("foo@bar.com").setEnvAuthDomain("http://localhost")


  /**
   * I didn't really want to write <code>getContent(response) should be ("contentExpected")</code>...
   */
  implicit def makeResponseRicher(response: Response) = RichResponse(response)

  case class RichResponse(response: Response) {
    def content: String = getContent(response)
  }

  override def beforeEach() {
    helper.setEnvIsLoggedIn(false)
    setUpLocalServices
  }

  override def afterEach() {
    helper.tearDown

    clearCookies
  }

  private def setUpLocalServices {
    helper.setUp

    val userEmail = "foo@bar.com"
    val exampleList = new List(userEmail, "exampleList")
    val exampleItem = new Item(exampleList, "exampleItem")

    exampleList.insert
    exampleItem.insert
  }

  private def withLoggedIn[T](tests: => T) {
    helper.tearDown
    helper.setEnvIsLoggedIn(true)
    setUpLocalServices

    tests
  }

  test("Lists.index when logged in") {
    withLoggedIn {
      val response = GET("/lists")

      response.status should be (200)
      response.content should include ("exampleList")
    }
  }

  test("Lists.index when not logged in") {
    val response = GET("/lists")

    response.status should be (302)
  }

  test("Lists.show") {
    withLoggedIn {
      val exampleList = ListOp.all.fetch().get(0)
      val response = GET(newRequest, "/lists/" + exampleList.id)

      response.content should include ("exampleItem")
    }
  }
}

テスト実行方法

play test

を実行して、http://localhost:9000/@testsにアクセスして、ListsTestを選択し"Run tests"をクリック

@ymnkさんのサンプルに上記FunctionTestを追加して、GAEモジュールのバージョンを上げたものをGitHubに上げておきます。

https://github.com/mumoshu/lists-with-play-scala-gae

トラックバック - http://d.hatena.ne.jp/mumoshu/20101212/1292167593