CakePHP

[Play!][CakePHP]Selenium周りを整備する

Seleniumに関しては、前々からちょこちょこと意識していたものの、本格的に使っていこうと思い出したのはPlayFrameworkを使い始めてから。

Seleniumが完全に統合されており、かっこよすぎる

というのも、PlayFrameworkではデフォルトでSeleniumによるテスト環境が整備されており、ちょちょいのちょいで始める事が出来る。この偉大な環境に僕は心酔し、Seleniumの世界へのめりこむこととなった。僕は元々フロント周りに強い人であったので、フロント周りのテストにも強い人として更なるパワーアップに期待したいところである。

ということで、PlayFrameworkに関して言えば、インストール時に既にSelenium周りが整備されているので言う事はない。

問題はもう1つの溺愛フレームワークであるCakePHPのほうだ。
CakePHPは、現実案件が1.3系で進んでいることもあり、1.3ベースでの整備を考えていきたい。

ググった限りではSeleniumを使ったSimpleTestの話題はあまり存在せず、PHPUnitベースのものが普及しているようだった。
CakePHPは2.0からPHPUnitベースのテストとなるので、このあたりでPlayFrameworkのようなエレガントなユニットからWebテストまでをオートテスティングできるような統合がされて欲しいと思っている(っていうかもうされている?よく追えてない)

さて、CakePHPでSeleniumという場合、

http://cakebaker.42dh.com/downloads/

にて、Selenium Helperとかそういうものが配布されており、これに関する言及もいろいろなところでされているが、いかんせんネタ自体が2006年や2007年のものが多くて不安になる。

CakePHPに限らずSeleniumをPHP環境で使う場合は、Selenium IDEを使ったHTMLベースのテストをすると、何の障害もなく進む。

そうではないPHPUnitを使った場合でも、まずSelenium RCサーバーをセットアップして起動を行い(Selenium RCのセットアップはググるといろいろ出てくるので割愛)、

$ java -jar ~/selenium/selenium-server-standalone-2.11.0.jar

コマンド上からPHPUnitを起動する(PHPUnitのセットアップはググるといろいろ出てくるので割愛)ことで、実現ができる。

$ phpunit --verbose /var/www/htdocs/hoge/app/tests/selenium/myapp.php

上記のPHPコードは、こんな感じだ。

<?php
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
class WebTest extends PHPUnit_Extensions_SeleniumTestCase {
    protected function setUp() {
        $this->setBrowser('*firefox');
        $this->setBrowserUrl('http://localhost:8808/');
    }

    public function testText() {
        $this->open('http://localhost:8808/users/');
        $this->assertElementContainsText('id=content', 'Hello');
    }
}
?>

このコードで行っていることは、通常のSeleniumのテストのコマンドライン版である下記

$ java -jar ~/selenium/selenium-server-standalone-2.11.0.jar -htmlSuite "*firefox" "http://localhost:8808/" "/var/www/htdocs/selenium_sample/app/tests/selenium/myapp.html" "/Applications/MAMP/htdocs/selenium_sample/app/tests/selenium/myapp_result.html"

の引数などをPHPから扱えるインターフェースにして、テストスイートを書く必要がなく、簡潔なコードでアサーションを書けるということである。

これをCakePHPに組み込む場合、まずテストケースをCakePHPのテスト関係が入っているフォルダに入れるというのが考えられる。続けて行う事としては、他のテストが実行される段階で同時に上記コマンドを実行するようにしてテストを走らせるか、もしくはクラスを使ってテストケース内で実行することにするかということになる。

個人的には、コマンドラインから走らせて結果がわかるというだけでも十分いいんだけどね。
PlayFrameworkのエレガントさを見てしまうと、CakePHPでもああいう感じで一気貫通で行いたいとか思ってしまったりなのだ。

※OSXでSelenium RCを動かしていると、Seleniumを終了させた後でサーバーであるJettyが終わらずに残ってしまうことがあり、ここはちょっと困ったなあという感じ。

Read More

TwitterのようにチップスをシェアするサイトTipshare

ちょっと前にやって大好評だったデザイナー向けCakePHP勉強会を一緒に開催したCakePHP仲間である、

@mon_satさん
と、
@konsanさん

TipshareはTwitter Bootstrapをベースにしている

Tipshare
http://tipshare.info/

というサービスを立ち上げたみたい。

「チップスをツイッターみたいな気軽な感覚で共有できるサイト」とのことで、注目のプロジェクトだ。
ちょっとしたチップスはブログで気合い入れてかくほどでもなく、かといって忙しいさなかにどこかにメモるでもなく日々埋もれていきがちだ。

そんなときにTipshareを使うといいだろうね。

エンジニア視点での見所は、Twitter Bootstrapを使って一気にUIを作っていること(UIを作り込んでから公開ではなく、とにかく作って公開し、ブラッシュアップしていくというスタイル)の他にも、今年末にリリースされる予定のCakePHP 2.0を使っていたり、NoSQL系のデータベースMongoDBを使っているあたりも要注目ポイントといえる。

また、GitHubのマイクロコード共有サイトGistに投稿されたコードを取り込むという機能もついているのが面白い。

Gistにはちょっとしたコードがいっぱい

URLを入力するだけで、Gistのフォーマットに変換されて表示される。

Gist経由でコードがカラフルに!

CakePHPコミュニティで使う人が増えているためチップスの中心がCakePHPに偏っているが、IT系ならどんなチップスでもいいとのこと。

ところで、このサイトの名称は、Tip Share(チップスをシェアしよう)なのかTips Hare(チップスを貼れ)なのかというのがコピーライティング的な部分の筆者的ツボだ。

あと、このペンギンのようなキャラクターはなんだろう?

名前募集中なのであろうか?

詳細は追って待て!

※一部、TipshareをTipstarとTypoしてしまった..

Read More

PlayFrameworkにCakePHPのPagesのようなものをつくる

今日はPlayFrameworkでCakePHPのPagesみたいな機能を創ってみましょうというお話。複数回で完結予定。

CakePHPを気に入っているところの1つにWebRootという機能とPagesというコントローラがある。

WebRootというのは、webrootというディレクトリ以下に置いたものをCakeのドキュメントルートにあるものとして読み込んでくれるというものだ。例えばwebroot/index.htmlというファイルを置くと、普通にindex.htmlが表示される。

つまり、フレームワークの機能を使わないサイトからの引越も容易というわけである。

WebRootのように使えて、なおかつより発展した形のものにPagesというコントローラーがある。普通にbakeすると最初から用意されているコントローラーで他のコントローラーと比べると異彩を放っている。

Pagesの機能は文字通り静的なWebページを提供するというものだ。WebRootとの違いは、テンプレートとして書ける点。
CakePHPのテンプレートは、ページ全体の構造を定義するlayoutファイルと、コンテンツ部分に挿入する各ビューから成り立つ(実際はもっと細かいが、端的に言うとそうなる)。

Pagesはコンテンツ部分さえ書いておけばよく、勝手にPagesコントローラーが認識してくれる。

例えばviews/pages/hoge.ctp(ctpという拡張子はCakePHPのテンプレートであるということ)というファイルを置くと、/pages/hogeというURLでアクセスした際にレイアウトを当てはめた上で表示してくれる。

こいつの主な使い方としては、Webアプリケーション内で必要な静的なファイル群があるだろう。例えばプライバシーポリシーなどだ。

もちろん、WebRoot以下に静的に書いてもいいが、そうすると、ページ全体が動的な場合(例えばログインしているかどうかを表示するとか)に結構面倒なことになる。

さて、こういった機能をPlayでやろうとするとどうするか。
実はPlayにはPagesコントローラーは用意されていないが、デフォルトで似たような機能が使われている。
playのドキュメントページなどがそれにあたる。

Play Frameworkのドキュメント画面

このドキュメントは、Play本体の「modules/docviewer/app/controllers/」というディレクトリにある「PlayDocumentation.java」で行われている。ポイントとなるのは、その中のpageというメソッド。

    @SuppressWarnings("unchecked")
    public static void page(String id, String module) throws Exception {
        File page = new File(Play.frameworkPath, "documentation/manual/"+id+".textile");
        if(module != null) {
             page = new File(Play.modules.get(module).getRealFile(), "documentation/manual/"+id+".textile");
        }
        if(!page.exists()) {
            notFound("Manual page for "+id+" not found");
        }
        String textile = IO.readContentAsString(page);
        String html = toHTML(textile);
        String title = getTitle(textile);
       
        List<String> modules = new ArrayList();
        List<String> apis = new ArrayList();
        if(id.equals("home") && module == null) {
            for(String key : Play.modules.keySet()) {
                VirtualFile mr = Play.modules.get(key);
                VirtualFile home = mr.child("documentation/manual/home.textile");
                if(home.exists()) {
                    modules.add(key);
                }
                if(mr.child("documentation/api/index.html").exists()) {
                    apis.add(key);
                }
            }
        }        
        render(id, html, title, modules, apis, module);
    }

これを書き換えて自分のクラスとかに適用すると同じようにPageが使える。
まずは、コントローラーを用意しよう。とりあえず「Site」という名前でコントローラーをつくってみた。

package controllers;
import java.io.File;
import play.*;
import play.mvc.*;
import java.util.*;
import models.*;
import play.libs.IO;
import play.vfs.VirtualFile;
public class Site extends Controller {
    public static void index() {
        render();
    }
}

こんな具合だ。
これにPageメソッドを加えるのだが、変更しないといけない点がいくつかある。とりあえず最低限動かしてみるためにページ群を置いておくパスを変更してみよう。PlayのDocViewerでは、Play本体以下にある「documentation」というフォルダを参照するようになっているので、自分のapp以下に変更する。「Play.applicationPath」という指定にすればOKだ。

続いて、今回はappのフォルダ直下に「page」というフォルダをつくったので、これを参照するようにする。とりあえず以下のように変更してSiteクラスに追加してみよう。

    @SuppressWarnings("unchecked")
    public static void page(String id, String module) throws Exception {
        File page = new File(Play.applicationPath, "page/"+id+".textile");
        if(module != null) {
             page = new File(Play.modules.get(module).getRealFile(), "page/"+id+".textile");
        }
        if(!page.exists()) {
            notFound("Page for "+id+" not found");
        }
        String textile = IO.readContentAsString(page);
        String html = toHTML(textile);
        String title = getTitle(textile);

        List<String> modules = new ArrayList();
        List<String> apis = new ArrayList();

        render(id, html, title, modules, apis, module);
    }

あと、「PlayDocumentation.java」からtoHtml()メソッドとgetTitle()メソッドは必要なので持ってくるようにしよう。

これで、例えばRoutesを

GET     /                                       Site.index
GET     /{action}/?                             Site.{action}

のようにすると、「http://localhost:9000/page?id=hoge」でアクセスが可能だ。

まあ、このままだとURLはかっこわるいし、いろいろと問題がある。加えてPage内にいれるファイルの形式についても触れておきたいので、それらはまた後で書こうと思うよ。

では。

Read More