Technote

by sizuhiko

CakeMatsuri 2009 TOKYO ふりかえり

<!– more –>昨年のCakePHPカンファレンスでは、一講演者としての参加だったのですが、今回は中の人として参加しました。
Blogを書くまでがイベントです、ということで、一息ついたところでの思いをKPTでまとめてみました。
イベント全般に関しては、スタッフで「ふりかえり」をしたので、当日の担当範囲中心に。

Keep:
二日間にわたり、かなり多くのかたと交流できたのが、今回のテーマ「交流」にマッチしていて、よかったのではないかと思います:yes:
特にこれまで勉強会などで会ったことのない初対面のかたと、いろいろな話ができたので楽しかったです。
コア開発者とも仲良くなれた気がしました!!:up:
また1日目の懇親会でJoelに先日のPHP勉強会で発表した「WebTestCaseでfixtureを使う」について、プレゼンできたことが良かったです。後で英語に翻訳してBakelyにアップするぜ!と宣言した(自分を追い込んだ)ので、頑張ります。:star:

Problem:
ワークショップ(実務者編)のユニットテスト基礎・実践は、実質基礎しかできませんでした:ko: 。イベントごとという意味ではオブジェクト倶楽部などの経験もあったので大丈夫かな?と思っていたのですが、ちょっと上級内容だったのかな?と。。。ただ資料としては、後で十分振り返ってできる内容になっているので、持ち帰ってゆっくり確認してもらえると、理解が深まるかな?と思います。
2日目の2次会は、grahamの向かいの席でがっつり英語談義だったのですが、途中から疲労:faint: とアルコール:beer: から頭の回転がおかしくなってきて、だんだんと聞き取りもできなくなってきてしまいました。

Try:
PHPerにはコマンドラインは難しい?というのもあったので、別の方式が良いのかな?とも思ったのですが、実務ではconsoleのテストを使ったりすることの方が多いので、やはりコマンドラインは捨てがたいのですよね。なので次回以降は、オブラブの合宿前にもやった「環境構築前夜祭」をできれば良いなと思います。地方からの参加で必ずしも前夜祭に参加できない(当日移動など)の方もいるので、資料は事前公開ということで。
そういったことも含め、実務者コースのハンズオンの感触は、今回かなり掴めたので、進め方などもっと良い形でできるようにしたいと思っています。
後は英会話ですなー、毎日仕事で使っていたときより明らかに落ちてるし(とは言っても元々たいしたこともないけど)。。。何か解決策を見つけて英会話力をアップしたいと思うのでした:) 。


最後に。
参加者の皆様の温かい声援や、Blogでの感想に助けられました。ありがとうございます。
ワークショップの持ち出し企画はやりたいなーと思いますが、ちょっとしばらくイベントは・・・と思ったりしてます:pが、来年には実施したいですね。
また皆様にお会いできるのを楽しみにしています。

わたしとCakePHP

<!– more –>CakeMatsuriを迎えるにあたってのリレー形式ブログ第2弾です。yandoさんの記事の後で恐縮ですが、これまでCakePHPと過ごしてきた日々を振り返ってみます。

CakePHPとの出会い

はじめてCakePHPという名前を聞いたのは、「10分で作るCakePHPアプリ」というRuby on Railsでも有名だった最速開発のスクリーンキャストをPHPでもやってみた、というものでした。

PHPは2000年頃に興味を持ってやっていたのですが、その後仕事はJavaやRubyが中心となり、疎遠となっていました。そのためPHPでもこんな事ができるのか!!と衝撃を受けました。

早速CakePHPをダウンロードし、動かしてみると、その導入のしやすさは(エラーメッセージの親切さも相まって)、これまでに体験したことのないものでした。早速blogチュートリアルを作って、アレコレいじっていた記憶があります。PHP熱が再発した瞬間でした。

ミニ案件との出会い

その直後、たまたま小さい案件で、言語は何でも良いという仕事に偶然と出会い、CakePHPの採用を決めました。当時のバージョンは1.0でした。当時は日本語の情報も少なく、Cake本体のコードを読みながら進めていたものでした。

書籍執筆のきっかけ

ちょうどその頃、同僚が「Web2.0ビギナーズバイブル」の共著を持ちかけてきました。この本はWeb2.0開発を始める人向けの本で、言語部分はそのものの入門とフレームワークの入門をペアにした企画でした。書籍としては(雑誌は既出でした)始めてCakePHPを取り上げることになりましたが、実は企画を依頼された段階ではsymfonyだったのです。CakePHPを使った後で比較のためにSymfonyも使ってみたことはあったのですが、どうしてもCakePHPでやりたいということを言って変更してもらった経緯がありました。そのときsymfonyで書いていたら今はどうしていたのかな?と思うことがあります。

これまで雑誌やメルマガの経験はあったのですが、初めての書籍はとても困難でしたが、ここから学ぶことも大変多く、その後「Webアプリケーションテスト手法」「CakePHPによる実践Webアプリケーション開発」の計3冊も共著させていただくことになったのは感謝でいっぱいです。

CakePHPコミュニティとの出会い

その後、yandoさん主催で始まったCakePHP勉強会に参加・発表したり、CakePHPカンファレンスで講演させていただいたり、様々なところでCakePHPユーザとの出会いを繰り返してきて新しい輪が構築できました。新しい輪は、仕事面でも指名でCakePHP案件をいただくこともありました。

直近では「CakePHP1.2ガイドブック」のトークショーを池袋ジュンク堂でやったのですが、これも楽しいイベントになりました。

そしてCakeMatsuriへ

今月末、10/30、10/31にCakePHPコミュニティの活動として、国内最大のイベントなるCakeMatsuri(昨年はCakePHPカンファレンス)が開催されます。

今年は2日間で、30日はこれまで何回か実施したハンズオンの延長線上になるワークショップ(すみませんもう定員に達しました)、31日は海外からコア開発者を招待しての講演や、CakePHPを使った事例発表などがあるカンファレンス(まだ参加者募集中です)から構成されています。

コミュニティの輪に身を投じてみて、私は様々な出会いやきっかけを得ることができました。皆様もCakeMatsuriに参加して新しい輪を広げてみてはどうでしょうか?

聴講者として参加するも、LTなど短い時間からでも、苦労したことを事例発表するなど、様々な参加スタイルがありますので、皆様の参加をお待ちしております。

CakePHPのWebTestCaseでfixtureを使う

<!– more –>9/30に行われた第46回PHP勉強会で発表した内容ですが、そのままブログで読めるように展開します。


経緯

CakePHPはユニットテスト実行時にテストデータを投入するfixture機能をサポートしています。CakePHPのユニットテストはSimpleTestをベースにしており、UnitTestCaseと、それを継承してWebブラウザ動作のテストを行うWebTestCase、2つのテストケースを持っています。CakeではそれぞれCakeTestCase、CakeWebTestCaseという名前になります。 ただしCakeWebTestCaseはWebTestCaseを継承しているだけで、何も機能拡張をしていません。

cake/tests/lib/cakewebtest_case.php

class CakeWebTestCase extends WebTestCase {
}

CakePHPオフィシャルのCookBookでも、以下のように解説されています。

CakeWebTestCase は、SimpleTest の WebTestCase をただ拡張したもので、特に機能追加はありません。SimpleTest の Web testing に関する文書中に記載がある全ての機能は、 CakeWebTestCase で利用できます。これはまた、 SimpleTest が持つ機能以外のものは使えないことを意味します。すなわち、 CakeWebTestCase においてフィクスチャは利用できず、テストケースにデータベースに対する更新や保存が含まれていた場合、恒久的にデータベースの値が変更されることを意味します。テストの結果は、しばしばデータベースが持つ値に基づくので、テスト手順の一部としてデータベースが期待した値を持つことを確認してください。

しかし実際に開発の場面では、WebTestCaseを使って、ログインからの操作を試したり、データベースに依存した操作・結果を求めなければならない場面に遭遇します。そこで今回はfixutureを使えるようなWebTestCaseを作ってみます。

考察

今回fixtureを使えるWebTestCaseを作る上で、考慮したポイントは以下のとおりです。

  1. なるべく書かない
  2. Cakeのバージョンが変わっても簡単に移行したい
  3. いつかは本体に組み込んで欲しい

そこで、今回はUnitTestCaseを継承している、caketestcase.php を流用して差分のみ別クラスに作成することにしました。こうすると、後でCakeのバージョンが変わったても問題が少ないし、後で本体に組み込んでもらうときもわかりやすいと思ったからです。

実施

実施の手順は以下のとおりです。

  1. caketestcase.php をコピーして、
fixturablewebbasetestcase.php に変更
  2. 上記ファイルの「CakeTest」 部分を 「FixturableWebBaseTest」に変換
  3. FixturableWebBaseTestのスーパークラスをWebTestCaseに変更
  4. FixturableWebTestCase を作成
  5. bootstrap に初期化コードを追加
  6. 実際のテストケースを記述

それぞれの手順を詳細に解説します。

1.caketestcase.php をコピーして、
fixturablewebbasetestcase.php に変更

まず流用元のテストケースをコピして、継承する元になるクラスを作成します。

cd {アプリケーションのHOMEディレクトリ}
mkdir app/vendors/webtest

cp cake/tests/lib/cake_test_case.php app/vendors/webtest/fixturable_web_base_test_case.php

2.上記ファイルの「CakeTest」 部分を 「FixturableWebBaseTest」に変換

これはお使いのエディタを開いて、置換機能を使えばあっという間に完了です。 app/vendors/webtest/
fixturablewebbasetestcase.php を編集してください。

3.FixturableWebBaseTestのスーパークラスをWebTestCaseに変更

このままでは、単にCakeTestCaseの名前を変更しただけなので、そのスーパークラスをUnitTestCaseからWebTestCaseに変更します。

class FixturableWebBaseTestCase extends WebTestCase {
    // extends を変更する
}

4.FixturableWebTestCase を作成

手順3でWebTestCaseを使ったCakeTestCaseと似たものができました。ただしこのままではWebアクセス時にfixtureを読み込むようにはできません。 FixturableWebBaseTestを継承したクラスを作成します。このクラスでは、Webテスト中なのか、通常動作中なのかを判定するロジック(CakeTestCaseからの差分)を記述します。 またWebテスト中なのか、そうでないのかを判定するために、以下の方法を検討しました。

  • tmp/tests 以下のファイルで識別する
  • UserAgentを使って識別する
  • HTTP独自ヘッダで識別する

まずUserAgentでテスト中か判断する方法を思いついたのですが、Webテストの場合UserAgentそのものを判断してビューを切り替えたり(特に携帯など)するので、これは方法としては最適ではないと判断し却下。 HTTP独自ヘッダは、やはり通常運用しているときにセキュリティホールを作る事にも繋がるので、微妙なところです。ただし通常(本番)運用する場合はCakeのデバッグレベルを0にして運用すると思うので、独自ヘッダの判定前にデバッグレベルを判定すれば問題ないということも言えます。 そこで今回は、tmp/tests以下にテンポラリファイルを作って判定するという方法を採用しています。 app/vendors/webtest/fixturablewebtest_case.php

<?php
App::import('Vendor', 'webtest' . DS . 'fixturable_web_base_test_case');

/**
 * FixturableWebTestCase class
 */
class FixturableWebTestCase extends FixturableWebBaseTestCase {
    /**
     * @overwrite 
     */
    function startCase() {
        $this->_lockWebTesting();
    }
    /**
     * @overwrite 
     */
    function endCase() {
        $this->_unlockWebTesting();
    }
    /**
     * bootstrap.php から呼び出す
     */
    function initIfTestMode() {
        if(file_exists(FixturableWebTestCase::_getLockFileName())) {
            parent::_initDb();
            Configure::write('Acl.database', 'test_suite');
        }
    }
    /**
     * TMPファイルを作成して、Webテスト中であることを宣言
     */
    function _lockWebTesting() {
        touch($this->_getLockFileName());
    }
    /**
     * TMPファイルを削除して、Webテスト中でなくす
     */
    function _unlockWebTesting() {
        unlink($this->_getLockFileName());
    }
    function _getLockFileName() {
        return TMP.'tests'.DS.'fixturable.web.test.tmp';
    }
}

5.bootstrap に初期化コードを追加

最後の準備として、bootstrapに初期化コードを追加します。bootstrapはCakePHPで動作するすべてのアクションが必ず通過する最初のポイントなので、ここで利用するデータベースを切り替えるようにします。 app/config/bootstrap.php

if(($_SERVER['PHP_SELF'] != '/webroot/test.php') && Configure::read() > 0) {
  if(App::import('Vendor', 'webtest' . DS . 'fixturable_web_test_case')) {
    FixturableWebTestCase::initIfTestMode();
  }
}

bootstrapはテストコードをtest.phpから実行しようとした場合も通過してしまうので、このURLはフィルタする必要があります。また通常運用で呼ばれないようにデバッグ値が0より大きい場合にデータベースの切り替えを実行するようにします。

6.実際のテストケースを記述

これでWebTestCaseでfixtureが使える準備は整いました。後はテストケースを記述するだけです。 今回サンプルとして、私の執筆した「CakePHPによる実践Webアプリケーション開発」で作ったCalendarNoteをテストしてみます。

<?php 
App::import('Vendor', 'webtest' . DS . 'fixturable_web_test_case');

class UsersWebTest extends FixturableWebTestCase {
    var $fixtures = array('app.group', 'app.user', 'app.users_group',     'app.schedule', 'app.schedules_user',
        'app.aco', 'app.aro', 'app.aros_aco'
    );

    function startTest($method) {
        parent::startTest($method);
        Configure::write('Acl.database', 'test_suite');
        $this->addHeader('Accept-Language:ja');
    }
    function testLoginAndCheckSchedule() {
        $this->assertTrue($this->get('http://calendarnote.localhost/users/login'));
        $this->assertTitle(new PatternExpectation('/CalendarNote/'));
        $this->assertSubmit('Login');

        $this->clickSubmit('Login', array(
            'data[User][username]'=>'hide',
            'data[User][password]'=>'password',
        ));
        $this->assertText('Hidetoshi Nakata');
        $this->assertLink('ログアウト');

        $this->assertTrue($this->get('http://calendarnote.localhost/schedules/index/month/2009/01'));
        $this->assertText('2008年12月28日');
        $this->assertTrue($this->clickLink('10:00-12:00 Nengashiki'));

        $this->assertFieldByName('data[Schedule][title]', 'Nengashiki');
        $this->assertFieldByName('data[Schedule][contents]', 'In Japan, there are the New Year holidays and a New-Year's-greetings ceremony is performed to the first day of work.');

    }
    function endTest($method) {
        parent::endTest($method);
        $this->get('http://calendarnote.localhost/users/logout');
    }
}

このテストシナリオは以下のとおりです。

  1. ログイン画面にアクセスできるか検証する
  2. タイトルがCalendarNoteになっているか検証する
  3. ログインボタンが出ているか検証する
  4. ユーザ名hide、パスワードpasswordでログイン(サブミット)する
  5. 次の画面に、フルネームが表示されていて、ログアウトリンクがあるか検証する。
  6. 2009年1月の月単位スケジュール一覧に遷移できることを検証する。
  7. 2008年12月28日という文字列の表示を検証する。
  8. スケジュール詳細へのリンクが表示されることを検証する。
  9. フォームのタイトルに正しい値が入っていることを検証する。
  10. フォームのコンテンツに正しい値が入っていることを検証する。

テストコードに目を向けると、最初にApp::importでテストケースの親クラスを取り込んで、テストケースの親クラスをFixturableWebTestCaseに指定する以外、ほとんど通常のモデルなどのテストコードと書き方は同じだということに気がつくでしょう。特にfixtureの記法に関しては、まったく同じです。 これは「caketestcase.php をコピーして、
fixturablewebbasetestcase.php に変更」したために、CakeTestCaseと同じ機能が利用できるようになっているのです。 もちろん記述方法が同じなら、実行方法も同じで、http://localhost/test.phpのようにブラウザからの実行や、コンソールからの実行なども問題ありません。CakeMateを使ってTextMate上からもテストが実行できます。もちろんコンソールやTextMateなどCLI環境から実行する場合なども、裏でHTTPアクセスするので、Apacheは動作している必要があります。 Webテストで、どのようなブラウザ動作ができるのかは、本家のSimpleTestサイトか、こちらも私が執筆に参加した「Webアプリケーションテスト手法」にて確認してください。

最後に

本カスタマイズはPHP5.2.10、CakePHP1.2.5で検証しています。CakePHP1.2系であれば特に問題なく動作すると思いますが、うまく動作しない場合はコメントいただけると助かります。 今後の展開としては、まずこの記事を英語にしてBakeryにアップしたいと思います。 それをきっかけにコア開発者の目にでもとまったら、本体への組み込みなんか検討してくれるかもしれません。 もしかして一度やろうとしてやめたのかもしれないんですけど、Cake祭り(*1)でコア開発者が日本に来るので、直接聞いてみたいなと思います。


(*1) Cake祭り:昨年はCakePHPカンファレンスとして実施した、イベントの第2回目。今年もコア開発者が日本に来るので要注目のイベントです。今すぐ申し込みへ。 http://matsuri.cakephp.jp/

CakePHP Cafe LiveTalk は盛況のうちに終了しました

<!– more –>本日、ジュンク堂池袋店にて、CakePHP1.2ガイドブック刊行記念トークショー(CakePHP Cafe LiveTalk)で司会を勤めました。

相変わらずゆるーい感じの仕切りでしたが、1時間半という時間を感じさせない内容になっていたのではないでしょうか?

Cafe LiveTalkと銘打ったように「Cafeでの楽しいおしゃべり」的な雰囲気を出せたのではないかと思います。

花火大会やガンダムを見るBBQなど誘惑の多い、しかも真夏日な暑い一日でしたが、来場していただいた皆様に感謝です。

本日の模様はUSTなどされなかったのですが、後日動画サイトに一部アップされるかも?しれません。

参加者の皆様のBlogを見るのが楽しみやら、心配やら。あんまり本の話をしなかったしなー(汗)

またCakePHPオフ会や、PHPカンファレンス、CakePHPカンファレンスでお会いしましょう。ではでは。

CakePHP Cafe LiveTalk

<!– more –>今週末、CakePHP1.2ガイドブックの発売を記念して、トークイベントを行います。

安藤祐介・新原雅司・堂園俊郎著

『CakePHP1.2ガイドブック』(毎日コミュニケーションズ社)刊行記念トークセッション

「CakePHP Cafe LiveTalk」

安藤祐介(CakePHP1.2ガイドブック著者)×新原雅司(CakePHP1.2ガイドブック著者)×岸田健一郎(司会)

■2009年 7月25日(土) 19:00~

Firefox Add on サイト、羽田空港HP、JR東日本フードサービス、佐川急便HPなど大規模サイト開発においても実績のあるCakePHP。CakePHP公認ガイドとして好評を博した『CakePHPガイドブック』が最新バージョン1.2に対応し『CakePHP1.2ガイドブック』として生まれ変わりました。そこで刊行を記念し、『CakePHPによる実践Webアプリケーション開発』(2009年3月刊)の著者岸田氏も交え、CakePHPの達人たちによるトークセッションを開催いたします。7月にドイツで開かれるCakeFestBERLIN2009の模様や開発現場のプロならではの濃い話を十二分に語ってもらいます。すでにCakePHPを使っている人は勿論、これからCakePHPを使おうとしている人も必聴のトークセッションです。

詳しくはコチラを参照ください。

当日は私が司会を務めます。ガイドブックに込められた思いや、CakePHP最新動向、安藤さんによるCakeFESTレポートなど盛りだくさんでお送りしますので、ぜひご参加ください。参加確実の方は、ジュンク堂へ予約電話を入れてもらえると、人数把握できますので、よろしくお願いします。もちろん当日参加もOkです。

イベント終了後には懇談会も予定しております。懇談会については、コチラ(ATND)から参加表明ください。

皆様の参加をお待ちしております。