« 2010年8月 | メイン | 2010年10月 »

2010年9月28日

DOMろうTouchコンテンツ
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

Flashエンジニアnao ozawaです。最近あんまりゴリゴリとFlashでコーディングしていません。なんでかなぁ!まるで働いていないみたいじゃないですか。一応ちゃんと仕事してるんですよ?
まぁ、そんな感じの毎日なので、実はここ数日、作業のかたわら、スマートフォン向けページのレイアウト方法を色々考えております。

・ブラウザの画面回転するのめんどくさくね?

 みなさんご存知のように、iPhoneを筆頭に最近のスマートフォンは、携帯を横に向けると、合わせてくるりと画面が横レイアウトに変わります。()く言う私も初めてソレに遭遇したときには驚きを隠せず、暫くは日がな一日iPhoneをくりくりと縦にしたり横にしたりして過ごしていたものです。それはそれは楽しかった。
が、しかし、最近スマートフォン向けのページを作る側にまわってみて、はじめて気がついたのです。

画面の幅が変わるのって、めんどくせぇ

もう喜んでる場合じゃない。あり得ないほどめんどくさかったのです。

 そう思ったきっかけはクリッカブルマップ。懐かしい響きですね。Flashが全盛を極めてからは、みんなすっかり利用されなくなってきていますが、残念ながらiPhoneではFlashは動きません。そのため、どうしてもクリッカブルマップを使いたかったんですが、ここで問題になったのが、画面の回転なのです。
 クリッカブルマップは画像上の絶対座標によってクリックエリアを決定しています。手持ちのiPhone4では画面が回転した時も横幅の拡大は「表示」の拡大という扱いになっているため座標はズレないのですが、実はXPERIA(Android1.6)では、横表示になった時に画面の再描画が行われるため、画像が縮小されます。
 じゃぁ、初めから画像の表示サイズをピクセルで指定しておくとか、機種ごとにテンプレートを用意すればいいんじゃない?という意見もあろうかと思います。自分でもそう思います。ですが、それも面倒じゃnそこは、エンジニアの端くれとして、スマートにコードを書いて解決したいと決意した次第なのです。
 そこでDOMの登場です。前置きが長くなりましたが、やっと本題です。

Q. DOMってなによ?
A. Document Object Modelの略で、簡単にいえばJavaScriptからHTMLタグとかCSSのパラメータとかを書き換えられるHTML5時代のスタンダード・ウェポン。iPhoneでも使えるよ!ジオン公国のツィマッド社が開発した陸戦用モビルスーツとは親戚関係。

・座標は比率から求めよう

 画面サイズも違えば縦と横での変化もあるページを、ひとつのコードで処理する。手っ取り早くコレを実現するには、画面幅に対して画像は80%のサイズ、というように、画面の横幅に対する比率を基準にしてしまえばOKです。
残りの数値は、全て画面が描画される都度に計算しましょう。なお、タグの属性の指定には「setAttribute」を使います。

[イメージタグ]
<img src="img/map.gif" border="1" usemap="#map_test" name="map_test" id="map_img" />
*サイズの指定はしない

[JavaScript]
// mapという新しいオブジェクトにID名「map_img」(imgタグで設定したID)を割り当て
var map = document.getElementById("map_img");
// mapというオブジェクトのwidth(横幅)にwindow.innerWidth(画面の横幅)の80%(* 0.8)を指定
map.setAttribute("width",window.innerWidth * 0.8);
これで、画像の横サイズは、画面の横幅が100ピクセルの時は80ピクセルに。640ピクセルなら512ピクセルになるわけです。

で、あとはクリッカブルマップの<area>タグで、クリックエリアを指定するcoords属性の座標を地味に計算。
例えば100x100pxの画像の(x=20,y=10)の場所に座標がある場合は、(x=100*0.2,y=100*0.1)と表せます。
キモは画像のサイズを1としてゼロ座標からポイントまでの長さを小数点で表すこと。この例では、aポイントは画像の横幅に対して1:0.2の位置、縦幅に対して1:0.1の位置となるという事ですね。
var ax = document.images["map_test"].width * 0.2;
var ay = document.images["map_test"].height * 0.1;
こんな感じ。見たまんまですね。比率は、Photoshop等で頑張って測りましょう。

こうしてax,ay〜dx,dyまで4点の座標を求めたら、createAttributeでタグに直接記述されていない(予め書かないでおいた)coords属性を作成し、適用します。
var map_area = document.getElementById("AreaID");
var set_coords = document.createAttribute("coords");
set_coords.nodeValue = ax + "," + ay + "," + bx + "," + by + "," + cx + "," + cy + "," + dx + "," + dy;
map_area.setAttributeNode(set_coords);
これで座標の指定は完璧。 座標指定のスクリプトをhoge()など好きな名前の関数にして、

window.onload = function(){
	hoge();
}
とすれば、ページを読み込んだ時に実行され、無事クリッカブルマップが動くようになります。

・回転した場合の処理

iPhoneには「onorientationchange」という魔法の呪文でブラウザの回転イベントを取れるのですが、Androidでは未対応なので、ここは素直に「onresize」(画面サイズが変化した場合のイベント)を使います。


window.onresize = function(){
	hoge();
}
これでOK。 本当は「回転しないように」出来れば素晴らしいのですが、今のところ、そんな神コードは思いつかない(無くはないけど面倒)ので、まぁ今日のところは、このへんで勘弁してやろう。iPhoneめ参ったか。フヒッ。

13:49 追記
書きなぐった記事を読みながら揚げパン食ってたら思いついた。回転しない(ように見える)のは、結構簡単にできるかも?上手くできたら後日投稿します。

・まとめ

今回はクリッカブルマップでしたが、この考え方で、ブラウザの横幅が変化してもレイアウトを維持できるコンテンツが作成可能です。
使い方は色々あるので、ぜひ皆さんもDOMってみてください。

つーかiPhoneでFlash動くようになんないかなぁ!ジョブス爆発しろ!
この秋出ると噂の新型MacBookAirも、きっとまた買っちゃうんだからね!

では、また。

2010年9月24日

携帯とスマートフォンでsymfonyのテンプレートを切り替える
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。中村です。

先日、まちつく!mixi版がスマートフォンに対応しました。

スマートフォン対応にあたって、元々まちつく!が携帯専用アプリであったために変更しなければいけない点がいくつかありましたが、その中でも今回はテンプレートの切り替えについて書きたいと思います。

時間的な都合もあり方針としては、基本的には携帯用のテンプレートを表示できるようにして、よく使われる機能についてはスマートフォン用のテンプレートを用意して表示するようにしました。

※まちつく!で利用しているsymfonyのバージョンとは違いますが、ここでは最新バージョンの1.4.7の場合になります。

独自のviewクラスを定義する

どのページがスマートフォンに対応しているかを管理することなく対応したかったので、アクセスしている端末とテンプレートファイルの有無によって挙動を変更することにしました。読み込むテンプレートの切り替えを行うために独自のviewクラスを定義します。アプリケーション名がfrontendだった場合は apps/frontend/config/module.yml に次のような設定をします。

all:
  view_class: myPHP

次にここで指定したmyPHPクラスを apps/frontend/lib/myPHPView.class.php として作成します。

テンプレートを切り替える

myPHPView.class.php の内容は次のような感じです(※コードはあくまでもサンプルです)。

<?php
class myPHPView extends sfPHPView
{

    const TOUCH_TEMPLATE_SUFFIX = 'Touch';

    public function initialize($context, $moduleName, $actionName, $viewName)
    {
        $config = sfProjectConfiguration::getActive();

        $touchViewName = $viewName . self::TOUCH_TEMPLATE_SUFFIX;

        // スマートフォン用のテンプレートファイルパス
        $touchTemplatePath = $config->getTemplateDir($moduleName, $this->getTemplate())
                           . DIRECTORY_SEPARATOR
                           . $actionName . $touchViewName . $this->getExtension();

        // スマートフォンからのアクセスでかつテンプレートが存在する場合
        if ($this->isTouch() && is_file($touchTemplatePath)) {
            parent::initialize($context, $moduleName, $actionName, $touchViewName);
            $this->setDecoratorTemplate(strtolower(self::TOUCH_TEMPLATE_SUFFIX));
        } else {
            parent::initialize($context, $moduleName, $actionName, $viewName);
        }

        return true;
    }

    public function isTouch()
    {
        $ua = $_SERVER['HTTP_USER_AGENT'];
        return (preg_match('/iPhone/', $ua) || preg_match('/Android/', $ua));
    }

}

スマートフォンからのアクセスでかつスマートフォン用のテンプレートファイルがある場合は parent::initialize にてスマートフォン用のテンプレートを指定しています。

また、setDecoratorTemplate メソッドを利用して、デフォルトで読み込まれる apps/frontend/templates/layout.php を apps/frontend/templates/touch.php に変更してヘッダ・フッタも変更できるようにしています。

まとめ

sfPHPViewを継承することでテンプレート読み込みに条件分岐を追加しました。独自のviewクラスはテンプレートファイルの切り替えもそうですがviewの挙動を変更するのに有効です。

2010年9月14日

VMWare ESXi4 で開発サーバを仮想化しよう
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは、ryosukeです。

現在ウノウでは開発環境にVMware vSphere Hypervisor (ESXi4.1) を使ってプロジェクト単位で仮想サーバを構築しています(たぶん)。
私はインフラチームに関わってないので、実際のところ会社の開発サーバがどうなってるか詳細までは知らないのですが、今回は自宅の開発サーバでVMWare ESXi4を利用して仮想マシン作ってコピーで増やして開発サーバが沢山作れたよ、というところまで順を追っていこうと思います。

物理的に必要なものはサーバとして稼働するマシンと、クライアント(ハイパーバイザを操作するクライアント)として使うwindowsマシンと、DVDブランクメディアが1枚です。
内容は簡単ですが、ステップ数が多いので可能な限り箇条書きにします。

インストール

  • VMWare社のサイトでVMware vSphere Hypervisorをダウンロードします
  • ダウンロードしたISOイメージをディスクに焼いて、CD/DVD-ROMからサーバマシンを起動するとインストールが始まります
  • インストール自体も数分で終わると思います
  • インストールが上手く行ったらパスワードやネットワーク関連の設定をしておきましょう
    • 今後の説明用の例として、ここでは static IP address で 192.168.0.2 に設定したとします
  • ネットワークの設定が済んだらVMware vSphere Clientを使ってVMの準備をします
  • ESXiをインストールしたhost(ここでは192.168.0.2)にwindowsマシンからブラウザでアクセスします
  • ここからはwindowsマシンで作業を行ないます
  • VMware vSphere Clientのダウンロードリンク(Download vSphere Client)があるのでそこからダウンロードします
  • VMware vSphere Client をインストールし起動します (ESXi4からは日本語に対応してて分かりやすいです)
  • インストールが終了したら VMware vSphere Client を起動し、192.168.0.2にrootのパスワードでログインします
  • 証明書がインストールされていない為、警告が出ますがこの段階ではどうしようもないので「無視」
  • ライセンスの警告が出るのでライセンスキーを登録します
  • ダウンロード時にvmwareよりメールでライセンスの取得の案内が届いていると思うので、それに沿ってライセンスキーを取得しておく
  • ホーム/インベントリ/インベントリ/構成タブ/ソフトウェア/ライセンス機能/編集.../キーを入力
  • 取得しておいたライセンスキーを入力します

以上で VMware vSphere Hypervisor のインストールは終了です。
ここから、仮想マシンを作成します。

仮想マシンの作成

  • VMware vSphere Client のメニューから新規仮想マシンを作成します
    • インベントリ/ホスト/新規仮想マシン をクリック
    • ウィザードが開くので、構成は標準で適当なvm名とデータストアを指定します
    • ゲストOSはLinuxでCentos4/5 (64ビット)を選びました
      • ここは自分の好みで選んでください (以下centosで進めます)
  • 仮想マシン名を右クリックして、電源/パワーオン で起動します (ツールバーのパワーオンボタンからでもOK)
  • VMware vSphere Client を動かしている windows マシンに Centos のISOイメージをダウンロードします
  • ダウンロードしたISOファイルを作成した仮想マシンでロードします
    • 「仮想マシンのCD/DVDデバイスの接続切断(ツールバーにアイコンがあります)」から、CD/DVDドライブ1/ローカルディスクのISOイメージに接続...
    • まずはISOファイルの 1of7 を選択して開きます
  • コンソールを起動するとCentosのいつものインストール画面が表示されるので、普通にインストールします
  • ディスクを変更したりなんかしながらインストール終了したら晴れてvmの出来上がり

これで、仮想マシンの作成は完了です。

この手順を繰り返せばホストマシンのリソースが許す限りいくつでも仮想マシンを作ることができますが、毎度インストールしてると大変なので、この仮想マシンをコピーして新しい仮想マシンを作りましょう。


コピー元となる仮想マシンには自分が使いやすい標準的な設定やソフトのインストール、システムのアップデート等をしておきます。設定が終わったら、コピー元のvmはパワーオフにしておきます。

仮想マシンの複製

  • VMware vSphere Clientの構成タブをクリックする
  • ハードウェアの囲みからストレージを選択
  • 左に表示されたデータストア一覧から仮想マシンが保存されているデータストアを選択
  • 右クリックで「データストアの参照...」をクリック
  • ルートフォルダ直下にツールバーの「新規フォルダの作成」でフォルダを追加します
  • コピー元となるvm名のフォルダ以下の*.log ファイル以外をctrlキーを使っって選択し右クリックでコピーします
    • *.vmx, *.nvram, *.vmdk, *.vmxf, *.vmsd ファイルがありました
  • 先程作ったフォルダに貼り付けするとコピーが始まります
  • コピーされた vmx ファイルを右クリックし、インベントリへ追加
  • vm名とリソースプールを選択して終了です
  • データストアブラウザを閉じます
  • 仮想マシンリストに先程コピーしたvmが増えているので、パワーをオンにします
  • 起動中にメッセージが出るので、サマリからI_copied itを選びます
  • コンソールからログインし、ホスト名やネットワーク設定をそのvm用のモノに変更して再起動すれば新しい仮想サーバの完成です
  • コピーにかかる時間によりますが3分程度で新マシンの追加ができたのではないでしょうか

会社ではサクっと何台も仮想マシンを増やしたりするので、コピー部分はESXiホストにSSHでログインしてコピーするshスクリプトとかを書いて実行してるみたいです(たぶん)。いろいろ楽をする工夫はできそうです。

さいごに

端折っているので分かりにくい部分も少なくないと思いますが、ハードウェアの問題さえなければすんなり使えるので、これぐらいの作業が必要なんだ程度に読んで気軽に試してみていただければと思います。


複数台のサーバ構成で何かを試したいとき等に、個人でもこういった環境を用意しておくと気軽に試せて便利です。

2010年9月10日

Propel1.5で階層構造が扱えるnested_setビヘイビア
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

yukiです。
今回は去年やりました階層構造についてです。Propel1.5でビヘイビアとして実装されたので早速使ってみました。
以下の例ではPropel単体ではなく、symfonyのsfPropel15Pluginを利用しています。

前回と同様のデータを利用したいので、対象となるテーブルのschema.ymlを以下のように定義しましました。

% cat schema.yml
propel:
  jojo:
    _attributes: { phpName: Jojo }
    _propel_behaviors:
      nested_set:
      timestampable:
    id: ~
    name:
      type: varchar
      required: true
      primaryString: true

するとテーブルのCREATEのSQLは以下のように出力されます。

% cat lib.model.schema.sql

# This is a fix for InnoDB in MySQL >= 4.1.x
# It "suspends judgement" for fkey relationships until are tables are set.
SET FOREIGN_KEY_CHECKS = 0;

#-----------------------------------------------------------------------------
#-- jojo
#-----------------------------------------------------------------------------

DROP TABLE IF EXISTS `jojo`;


CREATE TABLE `jojo`
(
        `id` INTEGER  NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(255)  NOT NULL,
        `tree_left` INTEGER,
        `tree_right` INTEGER,
        `tree_level` INTEGER,
        `created_at` DATETIME,
        `updated_at` DATETIME,
        PRIMARY KEY (`id`)
) ENGINE=InnoDB;

# This restores the fkey checks, after having unset them earlier
SET FOREIGN_KEY_CHECKS = 1;

ご覧のように、tree_left, tree_right, tree_levelが作成されます。

実際の使い方ですが、以下のように通常のモデルのメソッドとしてビヘイビアメソッドが使えます。
コード中のコメントは出力結果です。

$george = new Jojo();
$george->setName('george');
$george->makeRoot();
$george->save();

/**
 * mysql> select id, name, tree_left, tree_right, tree_level from jojo;
 * +----+--------+-----------+------------+------------+
 * | id | name   | tree_left | tree_right | tree_level |
 * +----+--------+-----------+------------+------------+
 * |  1 | george |         1 |          2 |          0 |
 * +----+--------+-----------+------------+------------+
 * 1 row in set (0.00 sec)
 */

$jonathan = new Jojo();
$jonathan->setName('jonathan');
$jonathan->insertAsFirstChildOf($george);
$jonathan->save();

/**
 * mysql> select id, name, tree_left, tree_right, tree_level from jojo;
 * +----+----------+-----------+------------+------------+
 * | id | name     | tree_left | tree_right | tree_level |
 * +----+----------+-----------+------------+------------+
 * |  1 | george   |         1 |          4 |          0 |
 * |  2 | jonathan |         2 |          3 |          1 |
 * +----+----------+-----------+------------+------------+
 * 2 rows in set (0.00 sec)
 */

$george2nd = new Jojo();
$george2nd->setName('george the 2nd');
$george2nd->insertAsFirstChildOf($jonathan);
$george2nd->save();

/**
 * mysql> select id, name, tree_left, tree_right, tree_level from jojo;
 * +----+----------------+-----------+------------+------------+
 * | id | name           | tree_left | tree_right | tree_level |
 * +----+----------------+-----------+------------+------------+
 * |  1 | george         |         1 |          6 |          0 |
 * |  2 | jonathan       |         2 |          5 |          1 |
 * |  3 | george the 2nd |         3 |          4 |          2 |
 * +----+----------------+-----------+------------+------------+
 * 3 rows in set (0.00 sec)
 */

$george = JojoQuery::create()->findRoot();
var_dump(get_class($george)); //string(4) "Jojo"
var_dump($george->getName()); //string(6) "george"
var_dump($george->isRoot());  //bool(true)
var_dump($george->isLeaf());  //bool(false)

$jonathan = $george->getFirstChild();
var_dump($jonathan->getName()); //string(8) "jonathan"
var_dump($jonathan->isRoot());  ///bool(false)
var_dump($jonathan->isLeaf());  ///bool(false)

$george2nd = $jonathan->getFirstChild();
var_dump($george2nd->getName()); //string(14) "george the 2nd"
var_dump($george2nd->isRoot());  ///bool(false)
var_dump($george2nd->isLeaf());  ///bool(true)

このように、DoctrineのNestedSetビヘイビアに非常に近い操作感で扱えるので、階層構造を使う際にPropelを選択肢に入れることができるようになりました。
何かのお役に立てば幸いです。

2010年9月 7日

Nagios のログの日付を変換しながら読むには
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

こんにちは。kyagi です。

ウノウではサービスの稼働状況を監視するために Nagios を使用しています。ログファイル「/var/log/nagios/nagios.log」には監視活動の履歴が出力されるのですが、最初のカラムの日付フォーマットがエポック秒になっているため、障害発生時の時間を知るためには変換処理を行わなければなりません。この目的を達成する簡単なスクリプト nagioslogview.rb を書いたので、以下に公開しておきます。同様に小さなストレスを抱えている方の解決策になれば幸いです。

http://github.com/kyagi/nagioslogview/blob/master/nagioslogview.rb

使い方は以下になります。-f オプションで tail -f と同じように成長するファイルを追いかけて読むことができます。テスト時などにご活用いただければと思います。(なお tail 機能には rubygem の file-tail(http://flori.github.com/file-tail/) を利用していますので、別途インストールが必要です。)

■使い方(ファイルを指定しない場合はデフォルトの /var/log/nagios/nagios.log が対象になります)
 $ nagioslogview.rb
 $ nagioslogview.rb -f # ファイルを追いかけて読みます。tail -f と同様です。
 $ nagioslogview.rb /var/log/nagios/archives/nagios-09-07-2010-00.log # 指定したファイルを読みます。
■rubygems と file-tail のインストール方法
$ sudo yum install rubygems
$ sudo gem install file-tail
$ gem list | grep file-tail
file-tail (1.0.5)
■変換前(エポック秒のため実際の時間がわかりづらい)
$ less /var/log/nagios/nagios.log
(... snip ...)
[1274194965] Auto-save of retention data completed successfully.
(... snip ...)
■変換後
$ nagioslogview.rb
(... snip ...)
[Wed May 19 00:02:45] Auto-save of retention data completed successfully.
(... snip ...)

2010年9月 3日

Google App Engine for Scala
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

おはようございます。うちだです。

最近Scalaが世界中の一部の人達の間で盛り上がってるようですね。
私も以前のエントリーに書いたように、次にくる言語はScalaだと信じています。

5分くらいで知るScala

最近のプライベートな時間はGAE漬けでScalaの波に乗れていなかったのですが、
ふとGAEでScalaを使えばいいのではないかと気付き、試してみました。

Eclipseをダウンロード

どれをDLすればいいのか迷いながらClassicを選択

GAEのプラグインをインストール

http://code.google.com/intl/en/appengine/docs/java/gettingstarted/installing.html

SDKも一緒に入れましょう

Scalaのプラグインをインストール

http://download.scala-ide.org/update-current

GAEのプロジェクトをつくろう

Javaと一緒の手順ですね

Scalaを使えるように

プロジェクト名を右クリしてConfigure→Add Scala Natureを選ぶ!

サーブレットを用意しよう

src以下に作りましょう
右クリ→Other→Scala..→class

エラーがでた

どうやらScala Editorがバグってるようです。(20100903)
悲しいですがText Editorで開きます

package net.unoh.labs

import java.io.IOException
import javax.servlet.http._

class TestServlet extends HttpServlet {
  override def doGet(req: HttpServletRequest, 
                     resp: HttpServletResponse) {
    resp.setContentType("text/plain;charset=UTF-8")
    resp.getWriter().println("ウノウラボ!")       
  }
}

起動

http://localhost:8888/test

動いた!

次にやること

  • Slim3 Datastoreを使ってみる
  • デプロイしてみる

所感

これからの時代はGAE+Scalaですね!
私は使わないことにしますが。

2010年9月 1日

アジャイルな開発をチームでやってみた(2010年版) - その2
このエントリーをはてなブックマークに追加 このエントリーをlivedoorクリップに追加

テストコード書いてますか? HIROKIです。

murahashiに続いて、テストファーストを導入してみての振り返りをします。 まず、どうやってチームにテストファーストのスタイルを持ち込んだのか。

1.テストが重要だという共通認識を持つ。

前のプロジェクトではテストコードは、ほとんどありませんでした。 その中で、開発になれていない人や新たに人が投入され、 極少数ですが、デグレーションが起きました。

その経験を元にテストが重要だという共通認識を持つことができました。

2.プロジェクト開始時からテストファーストに踏み切る気持ちが必要

テストコードを書かなければコミットさせない。ぐらいの気持ちが必要です。 実際に何度も、本格的な実装が始まる前から口にしていました。 「うちのチームはテストを書かなければコミットを許さない。」と。

3.でも、テストコード書いたことないよ?

テストコードを書いたことのないメンバーもいるかと思います。 そんな人は下記の参考書を写経することから始めましょう。

symfonyを使うことが前提だったので、上記の本をlimeで置き換えて写経しました。

また、下記のような動画もとても参考になります。

4.そんなこと言っても、自主的に学習する人ばかりでないよ?

初日にペアプロしちゃいましょう。

そのチームでTDDに一番詳しい人と、TDD初心者な人をペアにして、 実装を始める初日かその前(できるだけはやく)にペアプロをしましょう。

ペアプロは1モジュール書き上げるくらいが良いかと思います。 そのチームで利用する開発環境の基本的なテストコードの書き方として後に参考にします。

私のチームではsymfony+memcached+lime+hudsonという環境でしたので、 どのタイミングでfixtureを流してmemcachedをクリアして、 どういったテストの書き方をしていくのか参考になるものを1つ作り上げました。 (細部に関してはチームみんなの意見をあつめて、検討してつくりました。)

これができれば他のモジュールにも適応して開発していくだけです。

5.じゃあ、やってみた経験から為になること教えてよ?

symfonyでテストファーストを実施してみた経験から具体例をあげてみます。

fixtureを理想的なものを最初につくっておく

テーブルの定義だけではなくて、 テーブルの中身のサンプルを全部のテーブル用意しておくこと。 データ自体は簡単なデータでよい。

モジュールをつくる度にあらたなfixtureをつくることを避けられる。 fixtureをつくるのが結構めんどくさい。 テストを書きたいのにデータがないというフラストレーションがたまる。 結局、つくらないといけないけど、他のモジュールで使っているfixtureと整合性がとれているのか?

規模が大きくなればなるほど、面倒なことになります。

limeのOKメソッドは使うな

bool値をチェックするメソッドとしてokメソッドがLimeには用意されていますが、 これを使用することを非推奨としました。

たとえば、テストが失敗したケースを考えると。

$test->ok(false);

実行画面

not ok 21
#     Failed test (./hogeTest.php at line 34)

という感じで返ってきます。ですが、isメソッドで書けば

$test->is(false,true,trueが返ってくるはず);
not ok 22 - trueが返ってくるはず
#     Failed test (./hogeTest.phpat line 35)
#            got: false
#       expected: true

という感じで出力が返ってくる。コメントもあるので、わかりやすい。

okメソッドだけだと情報が少なくて苦労するケースがあるので、面倒でもisメソッドで書くことにしました。

コメントではなくて、コードに含めろ

非推奨

// trueが返ってくるはず
$test->is($foo->bar(),true);
not ok 22
#     Failed test (./hogeTest.phpat line 35)
#            got: false
#       expected: true

推奨

$test->is($foo->bar(),true,trueが返ってくるはず);
not ok 22 - trueが返ってくるはず
#     Failed test (./hogeTest.phpat line 35)
#            got: false
#       expected: true

コメントを元にテストの場所や書いたテストの意図がわかるので、メソッドに含めましょう。

まとめ

build_status.png

このグラフがhudsonが1回目のビルドから529回目のビルドまでのテストにかかった時間と成功・失敗の結果です。

チーム全体で常にすべてのテストをパスしている状態を保つという意識とそれに伴う行動の結果です。

Files=59, Tests=3152

現時点で59ファイル、3152のテストをパスしています。

赤色の部分も目立ちますが、テストが成長していることが目で見てわかります。 こうやって、テストに支えられていることも再認識することも大切なことなのかな。とも思います。

今回は、チームでテストファーストをやるためのステップと、少し具体的な例を紹介してみました。


About 2010年9月

2010年9月にブログ「ウノウラボ by Zynga Japan」に投稿されたすべてのエントリーです。過去のものから新しいものへ順番に並んでいます。

前のアーカイブは2010年8月です。

次のアーカイブは2010年10月です。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

Zynga Japan