PHPに限らずアプリケーションの開発において非常に重要なプロセスがテストです。
テストを行わずに作成できるアプリケーションなど存在しません。
非常に重要で、金も期間もかかるのがテストフェーズなのですが、残念ながら世間ではたいしたものだとは思われていません。
まあ私もテスト苦手なんですが。
というわけでSymfonyにはプログラムのテストを行うライブラリが予め用意されています。
まずはユニットテストを行ってみましょう。
ユニットテストはひとつの関数、一つのメソッドが想定通りに動いているかをチェックするテストです。
とりあえずやってみます。
test/unit/JobeetTest.php
上二行は決まり文句なので、常に記述しておきます。
lime_testオブジェクトの適当なメソッドを実行するだけで簡単にテストが行えます。
第一引数はテストを行う件数です。
ここと実際行った件数が違う場合はそのことを教えてくれます。
第二引数は出力結果を色付けしてくれるのですがWindowsにはその機能がありません。Linux限定です。
実行は残念ながらコマンドライン限定。
> php test/unit/JobeetTest.php
1..1
ok 1 - This test always passes.
Looks like everything went fine.
上記lime_test::pass()は常に成功するというメソッドなので、実行結果もさくっと成功という内容になりました。
次はもう少しだけまともなテスト。
test/unit/JobeetTest.php
> php test/unit/JobeetTest.php
1..6
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
Looks like everything went fine.
lime_test::is()はふたつの引数が同一かどうかをチェックするメソッドです。
Jobeet::slugify()がうまく動作していれば、両者は一致します。
比較に失敗してしまった場合、次のようなエラーが表示されます。
> php test/unit/JobeetTest.php
not ok 1
# Failed test (.\test\unit\JobeetTest.php at line 5)
# got: 'sensio'
# expected: 'sensio1'
Looks like you failed 1 tests of 1.
この場合は第一引数が'sensio'、第二引数が'sensio1'なので不一致になりました、という意味になります。
ちなみに比較方法ですが、
がどちらもtrueだったので===ではなく==での比較のようです。
第三引数にメッセージを渡すこともできます。
test/unit/JobeetTest.php
> php test/unit/JobeetTest.php
# ::slugify()
ok 1 - ::slugify() converts all characters to lower case
Looks like everything went fine.
何処でエラーになったかがわかりやすくなったりする利点があります。
次にコードカバレッジ。
対象のテストで実行されていない行があるかチェックしてくれる、命令網羅用のチェック機構です。
> php symfony test:coverage --detailed test/unit/JobeetTest.php lib/Jobeet.class.php
>> coverage running C:\xampp\htdocs\src\php.../test/unit/JobeetTest.php (1/1)
lib/Jobeet.class 100%
TOTAL COVERAGE: 100%
現在Jobeetクラスにはメソッドがslugify()しかないので、Jobeet::slugify()を一回でも実行した時点でカバレッジ率100%になります。
適当にfunction hoge(){}とか一行足して確認すると、100%ではなくなります。
> php symfony test:coverage --detailed test/unit/JobeetTest.php lib/Jobeet.class.php
>> coverage running C:\xampp\htdocs\src\php.../test/unit/JobeetTest.php (1/1)
lib/Jobeet.class 83%
# missing: 17
TOTAL COVERAGE: 83%
この二つを利用して、コードのユニットテストを行うことができます。
さて、先にどのような挙動をさせたいかを記述し、その動作に合致するように実装を行っていくことをテスト駆動開発と言います。
ユニットテストはこの手法で開発を行うのに適した作りになっています。
まず動作してほしい動きを書きます。
個別記事に飛んだときはこのようなURLになるわけですが、
http://symfony.localhost/frontend_dev.php/job/sensio-labs/paris-france/1/web-developer
jobeet_job.companyを空白にしてみると、出力されるURLは以下のようになります。
http://symfony.localhost/frontend_dev.php/job//paris-france/1/web-developer
アクセスするとエラーになってしまいます。
このように空白になってしまう時に、Jobeet::slugify()によって'n-a'という文字列を挿入してもらいましょう。
test/unit/JobeetTest.php
現時点で実行すると、当然ながらテストは失敗します。
> php test/unit/JobeetTest.php
not ok 1 - ::slugify() converts the empty string to n-a
# Failed test (.\test\unit\JobeetTest.php at line 6)
# got: ''
# expected: 'n-a'
Looks like you failed 1 tests of 1.
このテストが成功するようにしつつ、これまでの動作は変化しないように実装を行います。
lib/Jobeet.class.php
最初の行を挿入しました。
再度テストすると、これまでのテストを含め全てのテストに成功しました。
出力されるURLも
http://symfony.localhost/frontend_dev.php/job/n-a/paris-france/1/web-developer
となり、アクセスすると正しく内容が表示されるようになりました。
さて、完成かと思えば実はこの実装は間違っています。
先ほど空にしたjobeet_job.companyを今度は'-----'にしてみましょう。
$textはemptyではないので最初のチェックはスルーされ、preg_replace()で全削除されてしまいます。
従ってリンク先はまたもやエラーが出る、このようなURLになってしまいます。
http://symfony.localhost/frontend_dev.php/job//paris-france/1/web-developer
これを修正するために、まずはテストコードを追加しましょう。
test/unit/JobeetTest.php
実行するとまたエラーになります。
lib/Jobeet.class.phpを再度修正します。
単にifの行を最後に持っていくだけです。
すると無事にテストを通過し、リンク先URLも正しくなりました。
チュートリアルでは更に通常の文字ではない文字でのslugifyも行っています。
iconv()はeウムラウトみたいなちょっと扱いに困る文字を似た形の文字に置き換えるという素敵な関数です。
あとおまけとして文字コード変換ができます。
'us-ascii//TRANSLIT'と指定することで、アスキー文字に存在しないものは削除あるいは翻字を行っています。
Symfonyの記事一覧
テストを行わずに作成できるアプリケーションなど存在しません。
非常に重要で、金も期間もかかるのがテストフェーズなのですが、残念ながら世間ではたいしたものだとは思われていません。
まあ私もテスト苦手なんですが。
というわけでSymfonyにはプログラムのテストを行うライブラリが予め用意されています。
まずはユニットテストを行ってみましょう。
ユニットテストはひとつの関数、一つのメソッドが想定通りに動いているかをチェックするテストです。
とりあえずやってみます。
test/unit/JobeetTest.php
1
2
3
4
|
require_once dirname(__FILE__).'/../bootstrap/unit.php';
$t = new lime_test(1, new lime_output_color());
$t->pass('This test always passes.');
|
上二行は決まり文句なので、常に記述しておきます。
lime_testオブジェクトの適当なメソッドを実行するだけで簡単にテストが行えます。
第一引数はテストを行う件数です。
ここと実際行った件数が違う場合はそのことを教えてくれます。
第二引数は出力結果を色付けしてくれるのですがWindowsにはその機能がありません。Linux限定です。
実行は残念ながらコマンドライン限定。
> php test/unit/JobeetTest.php
1..1
ok 1 - This test always passes.
Looks like everything went fine.
上記lime_test::pass()は常に成功するというメソッドなので、実行結果もさくっと成功という内容になりました。
次はもう少しだけまともなテスト。
test/unit/JobeetTest.php
1
2
3
4
5
6
|
$t->is(Jobeet::slugify('Sensio'), 'sensio');
$t->is(Jobeet::slugify('sensio labs'), 'sensio-labs');
$t->is(Jobeet::slugify('sensio labs'), 'sensio-labs');
$t->is(Jobeet::slugify('paris,france'), 'paris-france');
$t->is(Jobeet::slugify(' sensio'), 'sensio');
$t->is(Jobeet::slugify('sensio '), 'sensio');
|
> php test/unit/JobeetTest.php
1..6
ok 1
ok 2
ok 3
ok 4
ok 5
ok 6
Looks like everything went fine.
lime_test::is()はふたつの引数が同一かどうかをチェックするメソッドです。
Jobeet::slugify()がうまく動作していれば、両者は一致します。
比較に失敗してしまった場合、次のようなエラーが表示されます。
> php test/unit/JobeetTest.php
not ok 1
# Failed test (.\test\unit\JobeetTest.php at line 5)
# got: 'sensio'
# expected: 'sensio1'
Looks like you failed 1 tests of 1.
この場合は第一引数が'sensio'、第二引数が'sensio1'なので不一致になりました、という意味になります。
ちなみに比較方法ですが、
1
2
|
$t->is(1,'1');
$t->is(1,true);
|
第三引数にメッセージを渡すこともできます。
test/unit/JobeetTest.php
1
2
3
|
$t->comment('::slugify()');
$t->is(Jobeet::slugify('Sensio'), 'sensio'
, '::slugify() converts all characters to lower case');
|
> php test/unit/JobeetTest.php
# ::slugify()
ok 1 - ::slugify() converts all characters to lower case
Looks like everything went fine.
何処でエラーになったかがわかりやすくなったりする利点があります。
次にコードカバレッジ。
対象のテストで実行されていない行があるかチェックしてくれる、命令網羅用のチェック機構です。
> php symfony test:coverage --detailed test/unit/JobeetTest.php lib/Jobeet.class.php
>> coverage running C:\xampp\htdocs\src\php.../test/unit/JobeetTest.php (1/1)
lib/Jobeet.class 100%
TOTAL COVERAGE: 100%
現在Jobeetクラスにはメソッドがslugify()しかないので、Jobeet::slugify()を一回でも実行した時点でカバレッジ率100%になります。
適当にfunction hoge(){}とか一行足して確認すると、100%ではなくなります。
> php symfony test:coverage --detailed test/unit/JobeetTest.php lib/Jobeet.class.php
>> coverage running C:\xampp\htdocs\src\php.../test/unit/JobeetTest.php (1/1)
lib/Jobeet.class 83%
# missing: 17
TOTAL COVERAGE: 83%
この二つを利用して、コードのユニットテストを行うことができます。
さて、先にどのような挙動をさせたいかを記述し、その動作に合致するように実装を行っていくことをテスト駆動開発と言います。
ユニットテストはこの手法で開発を行うのに適した作りになっています。
まず動作してほしい動きを書きます。
個別記事に飛んだときはこのようなURLになるわけですが、
http://symfony.localhost/frontend_dev.php/job/sensio-labs/paris-france/1/web-developer
jobeet_job.companyを空白にしてみると、出力されるURLは以下のようになります。
http://symfony.localhost/frontend_dev.php/job//paris-france/1/web-developer
アクセスするとエラーになってしまいます。
このように空白になってしまう時に、Jobeet::slugify()によって'n-a'という文字列を挿入してもらいましょう。
test/unit/JobeetTest.php
1
2
|
$t->is(Jobeet::slugify(''), 'n-a'
, '::slugify() converts the empty string to n-a');
|
現時点で実行すると、当然ながらテストは失敗します。
> php test/unit/JobeetTest.php
not ok 1 - ::slugify() converts the empty string to n-a
# Failed test (.\test\unit\JobeetTest.php at line 6)
# got: ''
# expected: 'n-a'
Looks like you failed 1 tests of 1.
このテストが成功するようにしつつ、これまでの動作は変化しないように実装を行います。
lib/Jobeet.class.php
1
2
3
4
5
6
7
8
9
|
static public function slugify($text){
//テキストが空であれば'n-a'を返す
if (empty($text)){ return 'n-a'; }
// 文字ではないもしくは数値ではないものすべてを-に置き換える
$text = preg_replace('/\W+/', '-', $text);
// トリムして小文字に変換する
$text = strtolower(trim($text, '-'));
return $text;
}
|
最初の行を挿入しました。
再度テストすると、これまでのテストを含め全てのテストに成功しました。
出力されるURLも
http://symfony.localhost/frontend_dev.php/job/n-a/paris-france/1/web-developer
となり、アクセスすると正しく内容が表示されるようになりました。
さて、完成かと思えば実はこの実装は間違っています。
先ほど空にしたjobeet_job.companyを今度は'-----'にしてみましょう。
$textはemptyではないので最初のチェックはスルーされ、preg_replace()で全削除されてしまいます。
従ってリンク先はまたもやエラーが出る、このようなURLになってしまいます。
http://symfony.localhost/frontend_dev.php/job//paris-france/1/web-developer
これを修正するために、まずはテストコードを追加しましょう。
test/unit/JobeetTest.php
1
2
3
|
$t->is(Jobeet::slugify(' - '), 'n-a',
'::slugify() converts a string that only contains non-ASCII characters to n-a');
|
実行するとまたエラーになります。
lib/Jobeet.class.phpを再度修正します。
単にifの行を最後に持っていくだけです。
すると無事にテストを通過し、リンク先URLも正しくなりました。
チュートリアルでは更に通常の文字ではない文字でのslugifyも行っています。
iconv()はeウムラウトみたいなちょっと扱いに困る文字を似た形の文字に置き換えるという素敵な関数です。
あとおまけとして文字コード変換ができます。
'us-ascii//TRANSLIT'と指定することで、アスキー文字に存在しないものは削除あるいは翻字を行っています。
Symfonyの記事一覧
PR
トラックバック
トラックバックURL: