忍者ブログ
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。



2026/04/10 18:56 |
Symfony-9日目
昨日はメソッド一つずつが正しく動作するかどうかという単体テストを行ったみたいですが、今日は機能テストを行います。
単に機能テストというと幅が広いですが、Webアプリの場合はリンクが正しいか、フォームに変な値を入れても大丈夫か、といったブラウザ上で正しく動くかどうかのテストが中心となります。
Webアプリを作成するときは必ずフォームに「'or 1 -- 」を入力してみるといったテストを行うことになりますが、はっきりいって面倒です。
機能テストを書くことで、そこらへんを自動的にやってくれるようになります。

まずtest/functional/frontend/ディレクトリを見てみると、知らない間にjobActionsTest.phpcategoryActionsTest.phpが出来ています。
モジュールを作成したときに、ついでに作成されたものです。
categoryActionsTest.phpを見てみると、/category/indexにアクセスするとcategoryモジュールのindexアクションが実行され、200のステータスコードが返ってくるはずだよ、的なことが書かれています。
実際http://symfony.localhost/frontend_dev.php/category/indexにアクセスすると404エラーになるので、このテストも失敗します。

>php test/functional/frontend/categoryActionsTest.php
# get /category/index
ok 1 - request parameter module is category
not ok 2 - request parameter action is index
# Failed test (test\sfTesterRequest.class.php at line 48)
# got: 'show'
# expected: 'index'
not ok 3 - status code is 200
# Failed test (test\sfTesterResponse.class.php at line 257)
# got: 404
# expected: 200
ok 4 - response selector body does not match regex /This is a temporary page/
1..4
Looks like you failed 2 tests of 4.


indexアクションが実行されるって書いてあるのに実際はshowアクションが起動するよ、ステータス200が返ってくるはずなのに404が返ってくるよ、という二つのエラーが返ってきました。

正しいカテゴリページはこんなかんじで、
http://symfony.localhost/frontend_dev.php/category/programming
実行されるアクションもcategoryActions::executeShow()なので動作テストの一部を現状に合わせて書き換えます。

test/functional/frontend/categoryActionsTest.php
1
2
3
4
5
get('/category/programming')->
with('request')->begin()->
  isParameter('module', 'category')->
  isParameter('action', 'show')->
end()


>php test/functional/frontend/categoryActionsTest.php
# get /category/programming
ok 1 - request parameter module is category
ok 2 - request parameter action is show
ok 3 - status code is 200
ok 4 - response selector body does not match regex /This is a temporary page/
1..4
Looks like everything went fine.


想定通りになりました。
ちなみに4行目は、本文中に「This is a temporary page」が入っていないかどうかをチェックしてOKだった、という意味です。


これからデータベースと接続を行うみたいなのですが、このJobeetTestFunctionalクラスの意味がわかりません。
何故かこのファイルだけtestディレクトリではなくlibディレクトリ内に設置しないといけないので気をつけましょう。

lib/test/JobeetTestFunctional.class.php
1
2
3
4
5
6
class JobeetTestFunctional extends sfTestFunctional{
  public function loadData(){
    Doctrine::loadData(sfConfig::get('sf_test_dir').'/fixtures');
    return $this;
  }
}


次は用意されていたjobActionsTest.phpを書き換えてみます。

test/functional/frontend/jobActionsTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
include(dirname(__FILE__).'/../../bootstrap/functional.php');
$browser = new JobeetTestFunctional(new sfBrowser());
$browser->loadData();
 
$browser->info('1 - The homepage')->
  get('/')->
  with('request')->begin()->
    isParameter('module', 'job')->
    isParameter('action', 'index')->
  end()->
  with('response')->begin()->
    info('  1.1 - Expired jobs are not listed')->
    checkElement('.jobs td.position:contains("expired")', false)->
  end()
;


何処にもinclude()とか書いてないのに何で勝手にlib/test/JobeetTestFunctional.class.phpが呼び出されるんでしょうね。
今回は'/'でアクセスした場合jobモジュールのindexアクションが呼び出され、出力されたHTML内に'.jobs td.position:contains("expired")'セレクタが存在しない、というチェックを行っています。
何れも正しいのでテストは成功します。
適当に存在するセレクタ、'.category .feed a'とかを入れてみると発見されてしまいエラーになります。

ところで':contains'って何なんだ?
CSS3には見あたらないんですが。
ヘルプにはそもそも何も無し。
どうにもこうにも。

PR


2010/01/29 22:27 | Comments(0) | TrackBack() | PHP
9時間9人9の扉 (非)プレイ日記7

これまでにわかっていること
・九年前にも同じ実験があった。しかも二箇所
・首謀者は1、他に三人の仲間がいた
・2が参加、少女が死んだ
・4はネバダで同じ実験に参加
・0は1に恨みを晴らすため今回の実験を行った
・14383421

残っている謎
・2がライトに驚いた理由
・四葉のクローバー
・24以外の前回の参加者が居ない理由
・偽0と偽2の正体
・378の扉が最初閉まっていた理由
・378で脱出せずに1368で脱出した理由
・2が棺の中で喋らなかった理由
・ジョンとルーシーの頭と心臓以外に左腕も残っていた理由
・偽0の番号が0ではなく6だった理由
・6の正体と眠り姫との関係
・モールスが読めない

ほぼ間違いない推測
・九年前に死んだ少女は3の妹
・24の絆は本物


…どうまとまるんだこれ???

……うーん、ひとつ妄想してみた。

0は3、目的としては当然1に復讐するためということで今回の実験を画策する。
ついでにその仲間も殺すために偽0と偽2に仕立て上げた。あとたぶん9も。
で、ついでにもうひとつ目的があった。
どいう経過かは知らないが、何故か吊り橋効果によって6の目線を自分に向けるためであった。
ところがたまたま5が居たためにアウトオブ眼中となってしまい「俺の負けだ」
これなら1368で脱出した理由も明らかだ。
うむ、素晴らしい推理だ。

問題点としては、3がそんな大金持ちそうには見えないし1を攫うのも無理そう。
あと最後に6が消えたとか眠り姫はどうなったとか。
全然駄目じゃないかとかいう批判は受け付けない。



2010/01/28 23:20 | Comments(0) | TrackBack() | ゲーム
アルファブロガー・アワード2009ゲーム部門のノミネートが酷い件

http://alphabloggers.com/2009/index.html#aba_gam
>「アルファブロガー・アワード2009」の投票を受け付けています!

jin115さん
オレ的ゲーム速報@刃

hatimaki73さん
はちま起稿

DAKINIさん
さあ?

忍之閻魔帳さん
忍之閻魔帳

あれっくすさん
N-Styles

まこなこさん
まこなこ


なにこのゲハ出張所。

一緒にされた忍とあれっくす(´・ω・)カワイソス



2010/01/26 21:07 | Comments(0) | TrackBack() | 戯言
Symfony-8日目その2
今回はDoctrineモデルクラスのユニットテストを行います。
データベースに接続したりしないといけないので前回より少々面倒です。

テスト動作は今まで作成してきたdev環境でも本番用のprod環境でもなく、test環境で動作します。
現在データが入っているデータベースはdev環境なので、これではデータベースを用いたテストができません。

ということでMySQLにtest用のデータベースを作成します。
> mysqladmin -uroot -proot create jobeet_test

Symfonyにtest環境用の接続設定を作成します。
> php symfony configure:database --name=doctrine --class=sfDoctrineDatabase --env=test "mysql:host=localhost;dbname=jobeet_test" root root

config/databases.ymlを見てみると、test用のデータベース定義が追加されています。
フィクスチャに書いてあるデータをtest環境に追加します。。
> php symfony doctrine:insert-sql --env=test

データの投入が終わりました。


ここから先は一気にいきます。

まずdata/fixtures/ディレクトリにあるファイルを全部test/fixtures/ディレクトリにコピペ。
test/bootstrap/Doctrine.phpおよびtest/unit/model/JobeetJobTest.phpを作成。

test/bootstrap/Doctrine.php
1
2
3
4
5
include(dirname(__FILE__).'/unit.php');
$configuration = ProjectConfiguration::getApplicationConfiguration(
    'frontend', 'test', true);
new sfDatabaseManager($configuration);
Doctrine::loadData(sfConfig::get('sf_test_dir').'/fixtures');
test/unit/model/JobeetJobTest.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
include(dirname(__FILE__).'/../../bootstrap/Doctrine.php');
$t = new lime_test(3, new lime_output_color());
 
$t->comment('->getCompanySlug()');
$job = Doctrine::getTable('JobeetJob')->createQuery()->fetchOne();
$t->is($job->getCompanySlug(), Jobeet::slugify($job->getCompany()),
 '->getCompanySlug() return the slufor the company');
 
$t->comment('->save()');
 
$job = create_job();
$job->save();
$expiresAt = date('Y-m-d', time() + 86400 * sfConfig::get('app_active_days'));
$t->is(date('Y-m-d', strtotime($job->getExpiresAt())), $expiresAt,
 '->save() updates expires_at if not set');
 
$job = create_job(array('expires_at' => '2008-08-08'));
$job->save();
$t->is(date('Y-m-d', strtotime($job->getExpiresAt())), '2008-08-08',
 '->save() does not update expires_at if set');
 
function create_job($defaults = array()){
  static $category = null;
 
  if (is_null($category)){
    $category = Doctrine::getTable('JobeetCategory')
      ->createQuery()
      ->limit(1)
      ->fetchOne();
  }
 
  $job = new JobeetJob();
  $job->fromArray(array_merge(array(
    'category_id'  => $category->getId(),
    'company'      => 'Sensio Labs',
    'position'     => 'Senior Tester',
    'location'     => 'Paris, France',
    'description'  => 'Testing is fun',
    'how_to_apply' => 'Send e-Mail',
    'email'        => 'job@example.com',
    'token'        => rand(1111, 9999),
    'is_activated' => true,
  ), $defaults));
 
  return $job;
}

テストを実行
>php test/unit/model/JobeetJobTest.php
1..3
# ->getCompanySlug()
ok 1 - ->getCompanySlug() return the slufor the company
# ->save()
ok 2 - ->save() updates expires_at if not set
ok 3 - ->save() does not update expires_at if set
Looks like everything went fine.


テストに成功しました。


何をどうやって何のテストに成功したの?

さあ?

全く意味がわかりません。


他のDoctrineクラス用のテストとかとても書けそうにないのでパス。


2010/01/25 22:45 | Comments(0) | TrackBack() | PHP
買ったものリスト 2010/01/24

今までいくらゲイツに貢いだのか調べてみようかと思ったが怖ろしくなったのでやめた。
20100124-msp


もえちり1
☆☆

堂高は全日本ミスコンビニ選手権が非常に面白かったんだが、全日本妹選手権は微妙だった。
なんとなく本屋にいたら見つけてしまったのでとりあえず買ってみたがこっちはもっと微妙。
都道府県代表と言っておきながら設定に全く意味がないのが残念。


新米女神の勇者たち7
☆☆☆

SW2.0のリプレイで一番好きなシリーズ。
他はたのだんもマージナルライダーもわざとらしさというか演技っぽさというかなんかそういうのが透けて見えてなあ。
と思ったらのっけからソラ離脱。大ショック!
あと生き返りがえらい気軽になったなあ。
8万ガメルの男の頃は一大事だったのに。


やおよろっ!2
☆☆☆

八百万全ての存在を擬人化するという壮大なプロジェクト。
なので毎回のタイトルが0000030人目、と7桁で表示されています。
いつ終わるんだよ。
ギャグは全体的に前作より微妙ですが、時折柱のあたりで奇怪な発言があったりしてなかなか目を離せません。


デュラララ!!5
☆☆☆☆

6巻と前後編のようなので、本書だけではちょっとよくわからない前振り的な内容が多いです。
至る所で誰かが何かに巻き込まれ、まだまだそれが有機的に繋がってこないバラバラな状態です。
まあ、この作者のことだから後編で全ての伏線が収束してくれることでしょう。
かなり期待。
あと相変わらず新キャラは人外魔境な連中ばかりです。


A列車でいこうHX

字が小せえ。
50インチで見ても小さいってどういうことだ。
この仕様でOK出した奴馬鹿だろ。

あと時折前触れ無くフリーズする。
今の本体でフリーズするのはこれとセインツロウ1だけだからソフトの問題だと思うんだがどうにかならんのか。
あ、あとパズルクエストで大量消ししたときにたまに止まるな。

また、肝心の列車がデフォではほとんど無く、全てDL販売だったりするのもがっかり。
ゲーム自体はいつものA列車なだけに残念な作り。



2010/01/24 18:52 | Comments(0) | TrackBack() | 買ったもの
9時間9人9の扉 プレイ日記6

9時間9人9の扉 プレイ日記5

※ネタバレを含むので未プレイの方は閲覧をご遠慮ください



2010/01/23 20:44 | Comments(0) | TrackBack() | ゲーム
Symfony-8日目
PHPに限らずアプリケーションの開発において非常に重要なプロセスがテストです。
テストを行わずに作成できるアプリケーションなど存在しません。
非常に重要で、金も期間もかかるのがテストフェーズなのですが、残念ながら世間ではたいしたものだとは思われていません。
まあ私もテスト苦手なんですが。

というわけで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);
がどちらも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の記事一覧


2010/01/22 23:36 | Comments(0) | TrackBack() | PHP
9時間9人9の扉 プレイ日記5

9時間9人9の扉 プレイ日記5

※ネタバレを含むので未プレイの方は閲覧をご遠慮ください



2010/01/21 21:22 | Comments(0) | TrackBack() | ゲーム
9時間9人9の扉 プレイ日記4
9時間9人9の扉 プレイ日記4

※ネタバレを含むので未プレイの方は閲覧をご遠慮ください


2010/01/20 23:04 | Comments(0) | TrackBack() | ゲーム
9時間9人9の扉 プレイ日記3

9時間9人9の扉 プレイ日記3

※ネタバレを含むので未プレイの方は閲覧をご遠慮ください



2010/01/19 23:12 | Comments(0) | TrackBack() | ゲーム

<<前のページ | HOME | 次のページ>>
忍者ブログ[PR]