Play+ScalaによるGAEアプリの開発方法について
(この記事は 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を作られていました。
このサンプルは、
- Play! Framework(Java用のWebアプリケーションフレームワーク)
- Play!用のScalaモジュール(MVCがJavaでは無くScalaで書けるようになります)
- Play!用のSienaモジュール(GAEに対応しているSienaというORMapperをPlay!から使えるようにするモジュールです。Play!のModelはGAEに対応していないため。)
- Play!用のGAEモジュール(Play!アプリを殆ど変更せずにGAEにデプロイできてしまう夢のようなモジュール)
を使って作られていて、GAEにデプロイすればそのまま動きます!
これから始める方は、このサンプルをベースに始めると良いと思います。
素晴らしいサンプルアプリをありがとうございます!>@ymnkさん
と、これだけではアレなので、今から始める方向けにplay+scala+gaeによる開発環境の構築からGAEへのデプロイまでの手順を、またガッツり実アプリを組む方向けにFunctionalTestの書き方を残しておきます。
環境構築〜サンプルアプリの実行とGAEへのデプロイ手順
- Play Framework - Build Modern & Scalable Web Apps with Java and Scalaとscalaのインストール。playとscalaにはパスを通しておいてください。
- play install scala
- play install gae
- play install siena
- http://code.google.com/intl/ja/appengine/docs/java/gettingstarted/installing.htmlに沿ってappengine-java-sdk-1.4.0をインストール。GAE_PATHを設定するのをお忘れなく。
- https://appengine.google.com/ でappengineへの登録とアプリケーションIDの払い出しをする
- git clone git://github.com/ymnk/lists-with-play-scala-gae.git
- war/WEB-INF/appengine-web.xmlにappengineのアプリケーションIDを書く
- ローカルで動かしてみる
play run
http://localhost:9000/にアクセスすると、appengineのdev_server上でPlay!アプリが動いています。
- 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