オブジェクト指向抜きで
   TDDしたいなら
 Limeを使うといいよ

 @tanakahisateru
自己紹介

                  タナカヒサテル
                  @tanakahisateru



PHPのフレームワークを開発しています

Pinoco
 https://github.com/tanakahisateru/pinoco
Lime Testing Framework
し…Symfonyだと!!
大丈夫、Limeは単独で使えるから。
        しかも…
超簡単
ダウンロード
http://trac.symfony-project.org/wiki/LimeTestingFramework
ファイル1個だけ
こんな感じ
テストと実装を足すとこんな感じ
テストの記述
<?php
require_once('lime.php');
require_once('utils.php'); // これからテストするソース

$lime = new lime_test();

$zenkaku_tel = "06−6543−9876";
$hankaku_tel = to_alphanum($zenkaku_tel);

$lime->is($hankaku_tel,"06-6543-9876");


utils-test.php

●単体ファイルをrequire_onceするだけで使えるようになる。
●テストケースにclassもfunctionも書かなくていい。

●assertEquals → is (短い)
テストを実行
$ php utils-test.php

1..2
PHP Fatal error: Call to undefined function to_alphanum()
in /Users/tanakahisateru/Desktop/limetest/utils-test.php on
line 8




コマンドラインからutils-test.phpスクリプトを実行すると、失敗。
to_alphnumメソッドが実装されていないことがわかる。

PHPでは「関数がない」とか「シンタックスエラーがある」
という、コンパイラを通る言語なら普通起こらないような
ミスが起こる。

すべてのソースをいちどrequireして関数コールを試しておく。
これコンパイルエラーのチェックと同じぐらい大事なこと。
仮の実装
<?php
function utils_to_alphanum($str)
{
    return $str;
}


utils.php
テストの実行
$ php utils-test.php

1..1
not ok 1
#     Failed test (./utils-test.php at line 10)
#             got: '06−6543−9876'
#        expected: '06-6543-9876'
# Looks like you failed 1 tests of 1.


もちろん失敗するよね。
実装する
<?php
function to_alphanum($str)
{
    return str_replace(
       array(
         '0','1','2','3','4','5','6','7','8','9','−'
       ),
       array('0','1','2','3','4','5','6','7','8','9','-'),
       $str
    );
}

utils.php

テスト駆動開発では、
 「テストコードをクリアする最少の実装」
を書くようにする。これポイント。最初から万能な実装を書いてしまうと、
テストを成長させるモチベーションがなくなってしまう。
テストの実行
$ php utils-test.php

1..1
ok 1
# Looks like everything went fine.

$lime->is($hankaku_tel, "06-6543-9876");
というテストが期待通り動いた。
テストケースの追加
<?php
require_once('lime.php');
require_once('utils.php');

$lime = new lime_test();

$zenkaku_tel = "06−6543−9876";
$hankaku_tel = to_alphanum($zenkaku_tel);
$lime->is($hankaku_tel, "06-6543-9876");

$zenkaku_email = "tanakahisateru@gmail.com";
$hankaku_email = to_alphanum($zenkaku_email);
$lime->is($hankaku_email, "tanakahisateru@gmail.com");
テストの実行
$ php utils-test.php

1..1
ok 1
not ok 2
#     Failed test (./utils-test.php at line 13)
#             got: 'tanakahisateru@gmail.com'
#        expected: 'tanakahisateru@gmail.com'
# Looks like you planned 1 tests but ran 1 extra.

# Looks like you failed 1 tests of 2.


もちろん新しく追加したテストは失敗する。
で、実装する…の?
<?php
function to_alphanum($str)
{
    return str_replace(
       array(
         '0','1','2','3','4','5','6','7','8','9','−',
         'a','b','c','d','e','f','g','h','i','j','k',
         'l','m','n','o','p','q','r','s','t','u','v',
         'w','x','y','z','_','.','@'
       ),
       array(
         '0','1','2','3','4','5','6','7','8','9','-',
         'a','b','c','d','e','f','g','h','i','j','k',
         'l','m','n','o','p','q','r','s','t','u','v',
         'w','x','y','z','_','.','@'
       ),
       $str
    );
}

utils.php
まあテストは成功だけど
$ php utils-test.php

ok 1
ok 2
1..2
# Looks like everything went fine.




この調子だと次のテストを書く気が起こらない。
リファクタリングなど
<?php
function to_alphanum($str)
{
    return mb_convert_kana($str, 'a');
}


utils.php

よし、書き換えよう。
たぶんこの関数で同じ変換いけると思う。たぶん…
おっとこれは!!
$ php utils-test.php

not ok 1
#     Failed test (./utils-test.php at line 9)
#             got: '06−6543−9876'
#        expected: '06-6543-9876'
not ok 2
#     Failed test (./utils-test.php at line 13)
#             got: 'tanakahisateru@gmail.com'
#        expected: 'tanakahisateru@gmail.com'
1..2
# Looks like you failed 2 tests of 2.
やばいやばい忘れてた
<?php
function to_alphanum($str, $encoding='utf-8')
{
    return mb_convert_kana($str, 'a', $encoding);
}
テストがあって良かった
$ php utils-test.php

ok 1
ok 2
1..2
# Looks like everything went fine.




機能が劣化したらすぐにわかる。これほんとTDDのおいしさ。
使い方はこちらで

http://www.symfony-project.org/gentle-introduction/1_4/ja/15-Unit-and-Functional-Testing
と、まあこんなやりかたで
PinocoはLimeでTDDしました
Lime単体でTDDやってみて
●
    コマンドラインのスクロール量がたいへん
    ●   テスト項目が20個超え始めるときつい
●
    テストスイートがない
    ●
        回帰テストをまとめて一発でやりたい
●
    汚れたグローバル環境の再浄化が要る
    ●
        実行スコープがグローバル
    ●
        連続して複数のテストを行うと前の状態に依存する
    ●
        続けてやったら単体テスト失敗するなどもってのほ
        か
というわけで作ったもの
●   Lime単体用のテストスイート
●   複数のLimeテストをまとめて実行できる
●   個々のテストは個別のPHPインタプリタで実行
    されるようにする
●   OKの場合はいちいち報告しない
ソースはこれだけ
使い方
実行した様子
/Users/tanakahisateru/Sites/pinoco/test/unit/test_vars.php:
# Pinoco_Vars Test
# toArray test
# import test
1..47
# Looks like everything went fine.


/Users/tanakahisateru/Sites/pinoco/test/unit/test_list.php:
# Pinoco_List Test
# push/pop
# shift/unshift
1..60
# Looks like everything went fine.


/Users/tanakahisateru/Sites/pinoco/test/unit/test_lazy.php:
# Pinoco_LazyValueProxy Test
(略)
それでも残る課題
●
    外部のシステムとの関係
    ●
        =単体テストにある本質的な問題
    ●
        言語ロジック外のシステムに依存すると、純粋な単
        体テストではなくなる。
    ●
        データ保存系のテストは次のテストの環境を書き換
        えてしまう。
    ●
        けれど自分の実装は実行の前提となる環境がないと
        動かせない、こまった。
単体テストで重要な技術
●
    モックオブジェクト
    ●
        本番用と同じインターフェースの偽オブジェクト
    ●
        オブジェクト指向的にいうとポリモーフィズム
    ●
        実際は何も書き換えないバージョンの外部環境
    ●
        テスト可能な設計とは、モックオブジェクトを受け
        入れられるインターフェースだということ
    ●
        はいオブジェクト指向きましたね
まあこういうことですよ




 Symfony2でまさかの乗り換え… orz
でも、
PHP is not for OOP coders only.



そこまで突き詰めなくてもまずはTDDしよう。
ユーザ層の幅広さ、それが「PHPらしさ」でしょ。
詳しくはGitHubのPinocoのソースにて。



    ありがとうございました。

関西Php勉強会のlimeの話