Furyu

[フリュー公式] Tech Blog

フリュー株式会社の技術ブログです

2011年04月26日

PlayでFunctional Test

テストの無いコードはレガシーコードだ!(挨拶

こんにちは、フリューの九岡です。
前回(PlayでRESTful Webサービス))に引き続き、今回はPlayのFunctional Testについて紹介します。

Functional Testって?

「Playのフレームワーク本体と、アプリケーション全体(Model-View-Controller)を通したテスト」です。

Playの公式ドキュメントでは以下のように説明されています。

A functional test is written using JUnit. In this kind of test you can test your application by accessing directly the controller objects.

(訳)
Functional Testを利用すると、Controllerを直接叩く事でアプリケーションをテストできます。

Play framework – Test your application

基本的なパターン

PlayのFunctionalTestでは、フレームワークとアプリケーションを通してテストするために、「HTTPリクエストをシミュレートして、レスポンスを検証」という方法を取ります。
コード例では以下の部分です。

GETHTTP GETリクエスト、OkはHTTPステータスコードの200 OKを意味しています。

HTTPリクエストをシミュレートするメソッドは、GETの他にPOST、DELETE、PUTも用意されています。
レスポンスの検証についても、assertIsOkのようなステータスコードに対するものの他に、レスポンスヘッダやレスポンスボディを検証するメソッドが用意されています。その他、利用可能なメソッドについてはPlayのAPIリファレンスに記載されています。
FunctionalTest (Play! API)

テストクラスの構成

テストクラスは以下の2段構成で、普通のJUnitテストケースと同じです。

  1. テスト環境の準備
  2. 各テストメソッドの実行

テスト環境の準備

Beforeアノテーションをつけたメソッドは各テストメソッドの実行前、BeforeClassはテストクラスの実行前にそれぞれ実行されます。(この二つ以外にもJUnit4が提供しているアノテーションは利用できます。詳しくはJUnit4のドキュメントを参照してください。)
通常、GET以外の操作はデータの追加・削除・更新など何らかの副作用を伴うはずです。そのような場合に、テストメソッドの実行順序でテスト結果が変わってしまうことのないように、BeforeやBeforeClassのタイミングで初期化を行うことをオススメします。
ここでは、テストクラスの実行前にテスト中にずっと使い回す一時ファイルの作成を行っています。また、各テストメソッドの実行前には、

でデータベースの内容を全てクリアした後、

でdata.ymlの内容をDBにロード、さらに各テストケースの事前条件としてPhotoを1件作成してDBに保存しています。

なお、テスト時にはapplication.confでテスト用として指定したDBが利用されます。play newでプロジェクトのひな形を生成したあと、特に変更していなければ、

のようにインメモリーデータベースになっているはずです。

テストメソッドの例

前回実装したPlayでRESTful Webサービスをつくるをネタに、いくつかテストメソッドを書いてみます。

リソースの参照(GET)

テキストの場合

上から順に、HTML・XML・JSONで/photosをリクエストしたときのレスポンスをテストしています。
assertContentTypeでレスポンスのContent-Type、assertCharsetで文字コードが期待通りであることを検証しています。
また、assertIsOkでステータスが”200 OK”であることを検証しています。レスポンスに応じたContent-Type、Charsetをセットするのは忘れがちなので注意したいところです。

バイナリの場合

preparePhoto()で用意しておいたPhotoの画像ファイルを取得するリクエストを送信して、ステータスが200 OK、Content-Typeがimage/pngで、かつ返却されたデータの長さが正しい事を検証しています。
(データの中身を比較しても良かったのですが、ここでは長さで必要十分と判断しました)

リソースの追加(POST)

ファイルを実際にアップロードするリクエストを送信して、レスポンスが正しい事、またアップロードしたファイルの情報がDBに保存されていることを検証しています。
レスポンス(JSON形式)の検証のため、Playに標準添付されているGson(Google製のJSONライブラリ)を利用しています。

リソースの更新(PUT)

Photoのtitleを変更するリクエストを送信して、200 OKが返却されることを確認しています。

リソースの削除(DELETE)

指定したIDのPhotoを削除するリクエストを送信して、200 OKが返却されることを確認しています。

総括

  • PlayでFunctional Testを書きました。
  • Functional Testでは、コントローラに対してGET/POST/PUT/DELETEなどのリクエストを送信して、ステータスコードやレスポンスボディなどが期待通りかを検証することでテストを行います。

Playにはこれ以外にUnit TestとIntegration Testも用意されていますが、投資対効果では個人的にこのFunctional Testが一番だと思います。
また、今回も

ソースコードはGitHubで公開していますので、参考にしてください。
この機会にぜひテストを書いてみましょう!


2011年04月25日

PlayでRESTful Webサービス

はじめまして。モバイル事業部の九岡です。
昨年9月にフリューに転職してきて、社会人としては今年で4年目になります。
JavaScriptとScala、そして御坂美琴(重要)をこよなく愛するWebエンジニアです。

本ブログでは、私が個人的に気になっている技術(主にプログラミング言語やWebフレームワークになると思います)を「○○を作ってみた」という形でご紹介していきます。

早速ですが、今回はPlay frameworkでRESTfulなWebサービスを作ってみました。

Play frameworkって?

Play! frameworkは、2009年頃から開発が続けられている、個人的にイチオシのJava・Scala向けWebフレームワークです。
発表当時は「Ruby on Rails for Java」のような取り上げられ方をされて話題になったのですが、何故か流行らなかった不遇のWebフレームワークです。
つい先日(2011/4/13)バージョン1.2がリリースされるなど、現在でも活発に開発が続いています。

RESTfulって?

RESTfulは簡単にいうと、「リソースごとにユニークなURIを振り、リソースに対する操作をHTTP METHODで区別する」ような構造のWebサービスのことです。

参考:REST – Wikipedia

詳細な説明は、実装例とあわせてします。

つくってみる

今回は、簡易な「画像共有サービス」をつくってみました。
巷の画像共有サービス(Flickrのようなもの)にはアカウント登録やコメント機能など色々なおまけがついていますが、ここでは画像を

  • アップロードできる
  • 検索できる
  • 閲覧できる
  • 変更できる
  • 削除できる

という5機能に絞ります。

これをREST的に解釈して、今回は以下の2つのリソースがあるということにします。

リソース URI
画像 /photos
/photos/{id}
画像ファイル /photos/{id}/data

そして、これらのリソースに対する操作をそれぞれAPIとして切り出します。
5つの機能をそれぞれ「リソースとそれに対する操作(HTTP METHOD)」に割り当てて、今回は以下のように決めました。

API(METHOD + URI) 機能
POST /photos アップロード
GET /photos 検索
GET /photos/{id}/data 閲覧(アップロードされた画像ファイルそのもの)
UPDATE /photos/{id} 画像のメタデータ(今回はタイトルのみ)を変更
DELETE /photos/{id} 画像を削除する

API一覧

これを早速Playで実装してみます。

ひな形の作成

まず、playでアプリの雛形を作成します。
どんなアプリを実装するときも、この方法で作成した雛形を少しずつ変更していけばOKです。

これでカレントディレクトリのrestというディレクトリ以下にアプリが作成されました。

routes追加

conf/routesに前述の5つのAPIのルート定義を書きます。

書式は「HTTP METHOD + パス + コントローラのクラス名.メソッド名」になっています。
指定したパスに、指定したメソッドでリクエストが来た場合、指定したコントローラのメソッドに処理を委譲する、という意味です。
直感的ですね!

コントローラの実装

routesに記載したメソッドをコントローラに実装します。

app/controllers/Photos.java

レスポンスまわりのコードはPlay独特かもしれません。
ポイントは以下の3つです。
これ以外は見た目通りではないでしょうか。

renderJSON(photos)

で、Photoのリストをjsonにシリアライズしてレスポンスに出力します。
簡単にいうと、出力したいオブジェクトをrenderJSONするだけで参照用のWeb APIが実装できます。

render(photos)

で、PhotoのリストをHTMLテンプレートに渡した上で描画します。
renderの引数に渡したphotosは、HTMLテンプレートからもphotosという名前で参照できます。

routesで以下のようなAPIを定義したことを覚えていますか?

ここでは、/photosのパスに

  • 拡張子なし
  • json
  • xml

の3パターンを許容しています。

render()を読んだ場合、リクエストされたときの拡張子に応じて描画するテンプレートを規約により勝手に決めてくれます。
例えば、Photos.indexの場合、拡張子を省略するとindex.html、.xmlならindex.xmlというテンプレートが使われます。

notFound

404 NOT FOUNDを返します。

モデルの実装

app/models/Photo.java

ここでも詳細は割愛しますが、ポイントは以下の3点です。

getter/setterが不要

Javaに詳しい方は違和感を感じるかもしれません。
Playのモデルではgetter/setterを書かなくてもOKです。
値のget/setしかしない上にサブクラスでオーバーライドすることもないのであればgetter/setterは要らないよね、というPlay開発者の思い切った判断ですね!

インターセプター(PrePersist, PreUpdate, etc)

Playは標準でいくつかのインターセプターを用意しています。
上記の例では、モデルをDBに永続化する前に必ず呼ばれるメソッドonSave()を実現するために使っています。

ビューの作成

app/views/Photos/index.html

ここでの実装ポイントは4点です。

タグ

#{名前}#{/名前}でPlayに組み込まれているタグを呼び出せます。

リンク

@{コントローラ.メソッド(引数)}は、指定したコントローラのメソッドに対応するURLに置き換わります。

変数の利用

${変数}で、コントローラのrender()に渡した変数の値が参照できます。

POSTパラメータの指定

先ほどコントローラで

のようなメソッドを定義したことを覚えていますか?

POSTするときに、photo.title、photo.dataというパラメータを指定すると、それぞれphotoにセットされた状態でコントローラのメソッドに渡されます。

app/views/Photos/index.xml

前述の通り、/index.xmlをリクエストした場合はこのテンプレートが使われます。

実行する

で、Playに組み込みHTTPサーバが起動します。

http://localhost:9000/photos
にアクセスすると、Photos/index.htmlの内容が表示され、ページ内のフォームから画像のアップロードができます。

そして本題のRESTfulなAPIを使ってみます。
RESTfulなWebサービスの利点として、HTTP通信ができて、JSONやXMLをデコードできる言語・環境ならどこからでもアクセスできますが、ここではお手軽にjQueryでアクセスします。

画像の更新であれば、

というようなコードになります。
routesで定義したとおり、APIはHTTP METHODとパスにより区別されます。
更新APIはroutesでPUT /photos/{id}と定義したので、jQueryからリクエストするときのURLも’/photos/1’のようになります。

また、削除であれば

検索であれば

という感じになります。

総括

  • Play frameworkでRESTfulなWebサービスを実装しました
  • Play frameworkのroutes, コントローラ、モデル、ビューの基本的な書き方を説明しました
  • ブラウザからWebページとして、HTTP経由でAPIとしてアクセスできることを示しました

駆け足の説明でしたが、Play frameworkを使うとお手軽にRESTfulなWebサービスが実装できることが、雰囲気だけでもわかっていただけたでしょうか?

ソースコードはGitHubで公開しています。
試しに動かしてみたいという方はぜひgit cloneしてみてください。

次回は、このアプリのユニットテスト・ファンクションテストについて書きます。
テストのないコードはレガシーコードだ!!(挨拶)


2011年04月25日

クラウド1ヶ月導入記 part 1 比較編

みなさん、こんにちは!フリューでモバイルサイト開発を行っている鷲見といいます。

とある案件が最終段階に差し掛かった2011年3月下旬、上司から

「次の案件はソーシャルアプリです。開発完了までは1ヶ月。アクセス規模はかなり大きめです。はい、君はインフラ担当です。頑張って」(意訳)

という非情な宣告を告げられました。

「俺この案件が終わったらゆっくりと小説を読むんだ」

なんて言ってたのは単なる死亡フラグでしかありませんでした。

といいつつもこの短期間で物理サーバを見積り・調達して揃えるのはなかなか難しいので、フリューではまだ導入していなかった、クラウド環境の構築に乗り出してみることにしました。

というわけで、これから『クラウド1ヶ月導入記』と題して記事を掲載していこうと思います。

ちなみに2011年4月11日現在このプロジェクトは進行中です!

では、まずどのクラウド環境を導入するのか、上司を説得させるための材料を出すために各種クラウドを比較してみたいと思います。

要件

まずは今回必要な条件は以下の通りです。

必須要件

  • 1ヶ月で開発を行えること(あまり新しい知識を勉強する時間がない)
  • Javaで開発できること
  • 今まで開発を行って来た案件でのフレームワークなどを流用できること
  • 結構大規模なアクセスをさばけること
  • スケールアウト・スケールインできること
  • サービスを落とさない構成が組めること

希望要件

  • 自動でスケールアウト・スケールインしてくれれば嬉しい
  • 価格が安ければ嬉しい

クラウド比較

クラウドと一概にいっても、IaaS,PaaS,SaaSといろいろあります。

  • IaaS (Infrastructure as a Service)
    • サーバやネットワークなどのIT基盤(Infrastructure)を、必要な時に必要な分だけ提供する
    • サービスの基板上にプラットフォーム、ソフトウェアを構築する
  • PaaS (Platform as a Service)
    • ソフトウェアを稼動させるためのミドルウェアなどのプラットフォームを、必要な時に必要な分だけ提供する
    • 基盤は特に意識する必要がなく、サービスのプラットフォーム上にソフトウェアを構築する
  • SaaS (Software as a Service)
    • ソフトウェアを必要な時に必要な分だけネット経由のサービスとして提供する

今回は特定のソフトウェアを使うのではなく、作成したアプリケーションを公開したいので、IaaSかPaaSとなります。

PaaSの検討

PaaSの代表的なサービスとしてはGoogle App EngineやForce.com、Windows Azure,herokuなどが挙げられます。

それでは上記要件と比較し、導入可否を検討してみます。

  • heroku
    • Rubyしか使えないので今回は該当しません。
  • Windows Azure
    • VisualStudioや.netframeworkに関する知識があまり無いので今回は見送ります。
  • Force.com
    • Force.com Codeに関する知識がほとんど無いので今回は見送ります。
  • Google App Engine
    • Javaで開発は出来ますが、既存フレームワークが使えないため今回は見送ります。

と、とりあえずPaaSの候補は全滅してしまいました。というわけでPaaSは諦め、IaaSという選択になります。

IaaSの検討

IaaSの代表的なサービスとしてはAmazon Web Services,ニフティクラウド、さくらVPSなどが挙げられます。
VPSはクラウドとは言いがたいのかもしれませんが、容易にインフラを準備できる環境ということで今回は比較対象としています。
この3つのサービスは必須要件をすべて満たしうるので、スペックやサービスについて比較します。

項目 Amazon Web Services ニフティクラウド さくらVPS
CPU 1ECU – 33.5ECU ※1 1vCPU – 4vCPU 仮想2core – 仮想4core
メモリ 613MB – 68.4GB 512MB – 16GB 512MB – 8GB
ディスク 160GB – 1690G
(増設EBSでTBオーダーで増設可)
30GB
(増設100GB – 1TB)
20GB – 240GB
IPアドレス 自動・固定 自動・固定 固定
DNS逆引き ×
ロードバランス ×(自前で導入する必要あり)
監視 ×(自前で導入する必要あり)
オートスケール ◯(制限あり) ×
有償サポート ×
日本語サポート
24時間サポート ◯(有償) ◯(有償) ×
その他 サービスが豊富 障害切り分け・復旧対応などもあり 値段が安い

※1 ECU=2007年製1.0~1.2GHzのAMD Opteron,Intel Xeon相当

とIaaSでもいろいろ差があることがわかります。

今回はサービスの幅が広く、スペックの選択幅も大きなAmazon Web Servicesが優勢です。

で同じことが出来るのであれば次に大事なのはお金ということで、次回はこの3つのIaaSでクラウド環境を構築した場合の金額の見積りについて記載していきたいと思います。