PHPでユニットテスト

仕事ではTDDの「テ」の字もないので、せめて趣味ぐらいはきっちりテストしていきたい訳です0;)
で、ユニットテスト用のツールを探してみました。
さくっと探してみるととりあえず次の二つが多く取り上げられてました。

  1. PHPUnit
  2. SimpleTest

試してみよ

と思ったのだけど、どうやらPEAR(ペア?)とライブラリのリポジトリから取ってくるのでその設定が必要みたい。

PEARをインストール

まずはPHPのインストールディレクトリへ移動
そこにある「go-pear.bat」を実行します。(以下全部Cygwinでやってます。)

$ cd C:\php
$ go-pear.bat

まず、PEARをsystem(どこから呼んでもこのPEARを使う)で使うか、local(2つ以上のPEARコピーを使い分けたい)で使うかを聞かれます。今回はsystemでいいので何も入力せずにEnterしました。
次に、PEARが使うディレクトリ配置の確認をしてきます。特に問題なかったのでそのままEnterしました。
最後にphp.iniにPEARの情報を追記するか聞いてくるのでphp.iniの場所が合っているか確認してYでEnter。
ホントにあっさりとPEARのインストールは完了しました。すごい!

PHPUnitを使ってみる。

xUnitのPHP版みたいです。とりあえず使ってみました。

PEARからインストール

まずはPEARのチャンネルにPHPUnitを追加するみたい。

$ pear.bat channel-discover pear.phpunit.de
Adding Channel "pear.phpunit.de" succeeded
Discovery of channel "pear.phpunit.de" succeeded

登録できたみたいなので、次にインストールしてみます。

$ pear.bat install phpunit/PHPUnit
Did not download optional dependencies: pear/Image_GraphViz, pear/Log, use --alldeps to download automatically
phpunit/PHPUnit can optionally use package "pear/Image_GraphViz" (version >= 1.2.1)
phpunit/PHPUnit can optionally use package "pear/Log"
phpunit/PHPUnit can optionally use PHP extension "pdo"
phpunit/PHPUnit can optionally use PHP extension "pdo_mysql"
phpunit/PHPUnit can optionally use PHP extension "pdo_sqlite"
phpunit/PHPUnit can optionally use PHP extension "xdebug" (version >= 2.0.0)
downloading PHPUnit-3.3.8.tgz ...
Starting to download PHPUnit-3.3.8.tgz (270,350 bytes)
.........................................................done: 270,350 bytes
install ok: channel://pear.phpunit.de/PHPUnit-3.3.8

成功したみたいです!
一応コマンドがあるか確認

$ phpunit.bat --help
PHPUnit 3.3.8 by Sebastian Bergmann.

Usage: phpunit [switches] UnitTest [UnitTest.php]
       phpunit [switches] <directory>

  --log-graphviz <file>    Log test execution in GraphViz markup.
・・・
テスト対象のクラスを書いてみる

ファイル名はクラス名.phpにする必要があるそうです。
では適当にBook.phpを作ります。

<?php
class Book {
	var $name_;
	/**
	 * 書籍名を設定します。
	 *
	 * @param string $name 書籍名
	 */
	function setBookName($name)
	{
		$this->name_ = $name;
	}
	
	/**
	 * 書籍名を取得します。
	 */
	function getBookName() 
	{
		return $this->name_;
	}
}
?>
テストクラスを書いてみる

テストクラスには

  1. PHPUnitと被テストクラスをインポート(?っていうのかな)
  2. testから始まる名前のメソッド

が必要です。
今回はBook.phpとBookTest.phpは同じディレクトリにあるのでrequire_onceにはファイル名だけ書いてます。

<?php
require_once 'PHPUnit/Framework.php';
require_once 'Book.php';

class BookTest extends PHPUnit_Framework_TestCase
{
	public function testSetGetBookName()
	{
		$book = new Book();
		$book->setBookName("PHP入門");
		$this->assertEquals("PHP入門", $book->getBookName());
	}
}
?>

では実行してみましょう。
今回はBookTest.phpがあるディレクトリで実行。

$ phpunit.bat BookTest.php
PHPUnit 3.3.8 by Sebastian Bergmann.

.

Time: 0 seconds

OK (1 test, 1 assertion)

成功したみたいです!
ってまあ、先にコード書いてからテストしてたらTDDじゃあないですね;;

アノテーションでのテスト宣言

さっきは「testから始まるメソッド名」が必要みたいに書いてしまいましたが、「@test」アノテーションをコメントブロックに書くのでOKだそうです。

<?php
require_once 'PHPUnit/Framework.php';
require_once 'src/Book.php';

class BookTest extends PHPUnit_Framework_TestCase
{
	/**
	 * @test
	 */
	public function setGetBookName()
	{
		$book = new Book();
		$book->setBookName("PHP入門");
		$this->assertEquals("PHP入門", $book->getBookName());
	}
}
?>

これはいいですね!

SimpleTestを使ってみる

SourceForgeからインストール

SimpleTestPEARからじゃなく普通にアーカイブ展開でいいみたい。以下のサイトから入手。
SimpleTest - Unit Testing for PHP Downloading SimpleTest
tar.gzファイルしかなかったんで、Cygwinで展開

$ tar zxvf simpletest_1.0.1.tar.gz 

で、これを今回はC:\php\PEARの下に入れました。理由はインクルードパスに通っているからってだけです。

テストクラスを書いてみる

被テストクラスはPHPUnitの時と同じBook.phpとします。
こちらの記事を参考にさせていただきました。
がるの健忘録 SimpleTest便利ざます〜

<?php
require_once 'simpletest/unit_tester.php';
require_once 'simpletest/reporter.php';

require_once 'src/Book.php';

class BookTest extends UnitTestCase
{
	public function __construct()
	{
		$this->UnitTestCase();
	}

	public function testSetGetBookName()
	{
		$book = new Book();
		$book->setBookName("PHP入門");
		$this->assertEqual("PHP入門", $book->getBookName());
	}
}

$test = new BookTest;
$test->run(new HtmlReporter());
?>

驚くほどPHPUnitに似ていますね。コードとしてはassertEqualsのsを取ったぐらいで。最後の2文はテストの実行コードみたいです。
では実行してみましょう。

$ php BookTest.php 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html>
<head>
<title>BookTest</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<style type="text/css">
.fail { background-color: inherit; color: red; }.pass { background-color: inherit; color: green; } pre { background-color: lightgray; color: inherit; }
</style>
</head>
<body>
<h1>BookTest</h1>
<div style="padding: 8px; margin-top: 1em; background-color: green; color: white;">1/1 test cases complete:
<strong>1</strong> passes, <strong>0</strong> fails and <strong>0</strong> exceptions.</div>
</body>
</html>

おお、なんかHTMLが吐かれました。てゆうか、自分で「HTMLReporter」って設定してるからか0;)
あれ?って事はブラウザから呼べばこれが表示されるんじゃない?そんな訳で呼んでみました。

http://localhost/simpletestsample/BookTest.php

画像ないんで意味ないんですけど、グリーンのバーが出てきました!なんかJUnitGUIを思い出します。
ちなみにTextReporterもあるみたいです。その場合はこんな感じ。

$ php BookTest.php 
BookTest
OK
Test cases run: 1/1, Passes: 1, Failures: 0, Exceptions: 0

まとめ

とりあえず使ってみた感想としては、「どっちも変わんねぇ〜」ですかね。HTMLでレポート出してくれるのでSimpleTestのが実行して楽しい気もします。でも、きっとPHPUnitも同じような事ができるんだろうな〜。
どっちもモックオブジェクトも作れるみたいなので、もうどっちがいいのか。。

WebDBPress vol48によると、どうやらDocTestというのもあるそうなのでそちらも調査してみる事にしましょう。

開発用エディタ

IDEは使わないとしてもメモ帳で開発するのもどうかと思うので、何かエディタを使う事にする。
適当に探してみて、とりあえず目に付いたのは以下の3つぐらい(もちろんIDEは除く)。

  1. Vi
  2. Emacs
  3. PHPエディタ
  4. サクラエディタ

Vi、Emacsは少し敷居が、、というかそっちを使いこなすのに時間をとられそうなので今回はパス。
PHPエディタは良さそうなのだけど、ちょっとしたIDEみたいな感じがしたので今回はパス。
で、結局いつもメモ帳の代わりに使っているサクラをカスタマイズする事にしました。
Linuxで開発する場合はViかEmacsを使う事になるんだろうな。。

カスタマイズ方法

カスタマイズとか初めてだったので以下のサイトを参考にほぼそのまま設定。
miau's blog? サクラエディタで PHP ファイルを編集するための設定まとめ
強調キーワードと強調キーワードヘルプファイルは以下のサイトのものを使わせていただきました。
MEMORVA サクラエディタのPHP強調キーワード設定方法(色分け表示)。入力補完機能の使い方。php.kwd、php.khp

ちょっと使ってみた

実際今までサクラをメモ帳代わりにしか使っていなかったので、まさかサクラがここまで素晴らしいエディタだとは思っていなかった、惜しいことした!
コード補完もさくさくできるし、キーワードヘルプも、ヘルプファイル呼び出しもさっとできる。これは素敵だ!

キーワードファイルの作成

標準関数の多いPHPなのでキーワードファイルにも更新が必要みたい。以下のサイトでキーワードファイルの作り方が紹介されていたのでメモ。
PHPスクリプト無料配布所 :: PHP.TO 0013. サクラエディタの強調表示用キーワードファイル(.KWD)を作成する方法

とりあえずとうぶんはサクラでがんばってみよう!

PHP5環境構築

まずは開発環境が無いと始まらないので環境構築をやってみる。
C#開発ではVisual StudioにどっぷりなのでPHPIDE無しでやるのに挑戦(挫折してIDE探すのも目に見えてるけど。。)

参考にしたのは秋元祐樹さん著のPHP WebサービスAPIコネクションズのAppendixのページ。(マッシュアップが流行った時に衝動買いしたらしく家にあった。)

PHP×WebサービスAPIコネクションズ

PHP×WebサービスAPIコネクションズ

調べてみるとどうやら開発するには以下の二つが基本的に必要みたい。

  1. PHP本体
  2. Apache(てゆうかWebサーバー)

で、今回はPHP5とApache2.2系で環境を作る。OSはWindows XPです。

PHPインストール

まずはPHP5だが、Windowsなのでバイナリをダウンロードするだけでいいみたい。
本家サイトのダウンロードページからZip Package版をダウンロード。

慣習らしいのでCドライブ直下にphpディレクトリを作り、そこでZIPの中身を展開した。
こんな感じ

C:php
   + php.exeとか

で、とりあえず実行するためにパスを通してみる。
環境変数に「PHP_HOME=C:\php」を作って、Pathの最後に「;%PHP_HOME%」をくっつけた。
Cygwin開いてでバージョン情報を出してみる。

$ php -v
PHP 5.2.8 (cli) (built: Dec  8 2008 19:31:23) 
Copyright (c) 1997-2008 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies

おっ、成功してる。

Apacheインストール

次はApacheなんだけど、考えてみるとPCにはすでにTracLightningが入ってるのでそれにApacheも付いてるのでそれを使うことにする。

Apacheのインストールは終わったとして次は設定ファイル編集が必要らしい。こっからは本に書いてある通りに進める。

PHPの設定

まずはphp.iniからだ。

C:\phpの直下にあるはずのphp.ini-distを複製してphp.iniを作り編集する。
まずはApacheのドキュメントルートの設定。TracLightningApacheを使うのでこんな感じ。

L.484

doc_root = "C:/TracLight/CollabNetSVN/httpd/htdocs"

次に拡張DLLライブラリの読み込み場所。どうやらC:\php直下にextというディレクトリがあるらしいが、ホントにあって中にDLLがわんさか入ってた。で設定するのはこんな感じ。

L.491

extension_dir = "C:/php/ext"

php.iniを「extension」で検索すると拡張DLLが定義されてて、それのコメント外して使えるようにするみたい。そこに無いDLLも、extディレクトリに入れて、php.iniに追記すれば使えるようになるみたい。

次に「magic-_quotes_gpc」。これをONにしておくと、フォームからの入力値を自動でエスケープしてくれるっていう素敵なものらしいんだけど、けっこう予想外の動きしたりしてありがた迷惑らしい。。なのでOFFにするそうです。

L.445

magic_quotes_gpc = On ; OnだけどOFF

最後にマルチバイトを扱うための設定。「mbstring」という項目に以下の設定をするそうです。(もはやなすがまま。。)

L.1190

mbstring.language = Japanese

L.1195

mbstring.internal_encoding = UTF-8

L.1202

mbstring.http_output = UTF-8

さて、php.iniの設定ができました!後はApacheの設定をするだけです。

Apacheの設定

Apachehttpd.confの設定をします。TracLightningのを使うなら以下にあります。

C:\TracLight\CollabNetSVN\httpd\conf\httpd.conf

まずはPHPのモジュールを読ませるように設定します。ただ、本ではApache2.0系を使ってたみたいで、TracLightningのApache2.2系とは設定が違って少しハマリました。。
LoadModule設定の最後に以下を追記します。

LoadModule php5_module C:/php/php5apache2_2.dll # php5apache.dllやphp5apache2.dllでは読み込めないみたい

次にphp.iniを読み込ませます。さっきのLoadModuleの直後に以下を書きます。

PHPIniDir "C:/php"

一度書き忘れて気づいたけど、phpinfo()関数読んだときの「Loaded Configuration File」の項目に入るみたい。

最後に.phpファイルのmimeタイプ(って何だ?)の設定をします。

AddType application/x-httpd-php .php

一応他にもいくつか設定はあったけど、とりあえずこれで動くみたい。Apacheを再起動してドキュメントルートに以下のソースを置いてみる。

index.php

<?php phpinfo(); ?>

おお、FireFoxから「http://localhost/index.phpで見れました!

これで明日から学習が進められるよ!

m4pファイルについて軽く調べる

最近、騒がしい職場に戻って生きたせいで、ipodを耳腺代わりに使っている。
あまり音楽を聴く習慣がなかったのだが、最近はこれがないとやってられない。
マクロスFの曲がタマンネ。


で、聞きたいCDをさらに集めてみたのだが、手に入らないCDがあった。
CDの名は『Piano Pieces "SF2" Rhapsody on a Theme of SAGA FRONTIER 2』。
すでに廃盤となっており、購入する場合、中古で我慢するしかない。
が、アマゾンだとプレミアが付いちゃって1万を超える。
中古で1万て。


そんなこんなで忘れようとしていたら、ファイルで取得する方法を見つけた。
iTune Storeで購入する方法である。


結局、欲しかったので購入。
で、ここからが本題。


iTuneを使っている人なら既に知っているかもしれないけど、このアプリはm4pファイルで音楽データを保存する。
今回購入したデータもその形式だったし、CDを読み込ませたらやっぱりその形式だった。
(もしかしたら設定の関係かもしれないけど。。。)
このm4pとは何だろうかと思い、少々調べてみた。


で、面白い記事を見つける。
http://melanges.duck.nu/2006/05/13_140659.php
なんでも、購入したm4pファイルはそのPCでしか再生できないとのこと。他のPCで再生する場合は、パスワードを求められるとか。
これ読んですげーとオモタ。これって著作権の侵害行為から来ているのだよね。
著作権の侵害行為は前からずっと問題になっていて、CDラジカセからしか読み込ませないようにしたり、あるいは、特別なアプリケーションからしか聞けなくしたり、そんなねじ曲がった歴史を歩んできた。
結局はその行為は廃止に向かっているようだけど。今度はこっちが主流となるのだろうか?もっとも、mp3に変換できるので、あまり意味がない気がするが。


話がそれた。m4pについて、だ。
m4pとは、AAC (エーエーシー、Advanced Audio Coding、先進的音響符号化 )にカテゴリされる。
AACについてはそれ以上はよくわからんかった。圧縮形式何ぞ調べる気になれん。。。)
iTune Storeから購入した場合、FairPlayで保護されたデータとなり、以下の仕様となる。

  1. パソコンでの再生:5台(コピーは無制限)
  2. 全く同じ内容の音楽CD作成:7枚
  3. iPodおよび対応携帯電話との同期:無制限
  4. 純正iアプリケーションでの利用:可
  5. ネットワークによる認証方式。解除、切り換えも可能。
  6. 複数アカウントの共存が可能。


先に述べた記事は1番のせいなんだろうね。
さて、軽くってこんな感じだよね?なので今日の調べごとはおしまい。
ちょうどBGMも終わったし。
BGM?もちろんそれは『Piano Pieces "SF2" Rhapsody on a Theme of SAGA FRONTIER 2』で。


参考wikipedia
コピーコントロールCD
http://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%94%E3%83%BC%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%ABCD
AAC
http://ja.wikipedia.org/wiki/AAC
FairPlay
http://ja.wikipedia.org/wiki/FairPlay


BGM
サガフロンティア2 ピアノアレンジ・バージョン‐ニコニコ動画(SP1)
ニコニコ動画のアカウントが無い人用
http://d.hatena.ne.jp/video/niconico/sm1251341

=Aoyagi=

思ったこと

私はさ、やりたくないこともあるけど、生きていくにはそれも仕方なし、と思って生きていて、
耐えることを是としてきた家の法をずっと守っていた訳さ。


でも、最近いい加減アホらしくなってきた。


人はさ、好きなことをやればいいんだよ。

これは投げやりな意見じゃなくて、うまく生きてゆくにはどうすればいいのか考えた結果の意見。
まいったね。26年生きて、やっと生きる意味が見つかった気がするよ。

まいったね。

=Aoyagi=

ズームコントロールいろいろ

スライダー、マップタイプボタンに加え、検索窓も出そろったところで、画面の基本機能追加にズームコントロールを設定してみましょ。

と言っても、ダブルクリックによるズームは初期値で有効になってますよね、でも本家のようにマウスホイールでグリグリするのが爽快なのだよ。

しかし!驚くべきことに、たった数行でそのすべては実現されてしまうのでした。さすがというかなんというか。。

まずデフォルトで有効にはなっているがダブルクリックのズームに関するAPI

// ダブルクリックズームの有効化
GMap2#enableDoubleClickZoom()

// ダブルクリックズームの無効化
GMap2#disableDoubleClickZoom()

// ダブルクリックズームが有効かどうか
GMap2#doubleClickZoomEnabled() : boolean

続いてなめらか〜にズームするようにする連続ズームに関するAPI。これはデフォルトでは無効です。

// 連続ズームの有効化
GMap2#enableContinuousZoom()

// 連続ズームの無効化
GMap2#disableContinuousZoom()

// 連続ズームが有効かどうか
GMap2#continuousZoomEnabled() : boolean

そしておまちかねのマウススクロールホイールでのズームに関するAPI。もちろんデフォルトは無効です。

// スクロールホイールズームの有効化
GMap2#enableScrollWheelZoom()

// スクロールホイールズームの無効化
GMap2#disableScrollWheelZoom()

// スクロールホイールズームが有効かどうか
GMap2#scrollWheelZoomEnabled() : boolean

さあ、眠いので超手抜き実装だ!

// ダブルクリックによるズームを有効にする
if (!map.doubleClickZoomEnabled()) {
    map.enableDoubleClickZoom();
}
// 滑らかな連続ズームを有効にする
if (!map.continuousZoomEnabled()) {
    map.enableContinuousZoom();
}
// マウスホイールのズームを有効にする
if (!map.scrollWheelZoomEnabled()) {
    map.enableScrollWheelZoom();
}

さあて、そしてサンプルはコチラ!

おおぉ!!グリグリできる!

さあ、次回あたりからはイベント系に入っていかなければね〜

ローカル検索コントロールを埋め込む

久々にGoogleMapを触ってみる。

今回は地図にローカル検索コントロール(+α)を埋め込んでみる。

で、結果はこれ。
http://sinjyukudotcs.web.fc2.com/gmapsample/080528_aoyagi.html

・・・すごくね?

コピペしただけなんだけど、こんな簡単にこんな高機能なコントロールが付いちゃっていいわけ?

・・・すごいね。


ちなみに、コントロールをマップ API アプリケーションに追加するには、以下のことが必要です。

  1. Google AJAX Search API URL を追加する。
  2. そのサービスのマップ API キーを使用する。
  3. そのコントロール オブジェクトのスタイルシートを読み込む。


最後に今回追加した個所を以下に記す。

<style type="text/css">
  @import url("http://www.google.co.jp/uds/css/gsearch.css");
  @import url("http://www.google.co.jp/uds/solutions/localsearch/gmlocalsearch.css");
  }
</style>
<script src="http://www.google.co.jp/uds/api?hl=ja&amp;file=uds.js&amp;v=1.0" type="text/javascript"></script>
<script src="http://www.google.co.jp/uds/solutions/localsearch/gmlocalsearch.js" type="text/javascript"></script>
// 縮尺のズームイン/ズームアウトのためのボタン及びスライダー
map.addControl(new GLargeMapControl());

// マップタイプを切り替えるためのボタン
map.addControl(new GMapTypeControl());

// 今回の主役、ローカル検索コントロール
map.addControl(new google.maps.LocalSearch(), new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new GSize(10,20)));