Silex の WebTestCase でハマるポイント

2011/05/20 01:18 追記
Github の Issues に報告したら間違いじゃないとの事。
https://github.com/fabpot/Silex/issues/95#issuecomment-1203730
WebTestCase で使用する場合は、http://silex-project.org/doc/usage.html#reusing-applicationsにあるように、独立して書けとの事。
$app->run(); はテストを壊すとの事なので、1 ファイルな場合は使わない方が良さそう。
という事で Smyfony2 の Request クラスを使ってディスパッチするように変更した。


id:brtriver さんに色々フォローして頂いた。ありがとうございます!


2011/05/20 03:00 追記
結局 Sf2 の Request だろうがなんだろうが、CLI で $app->run(); を動かすのがマズいみたい。
じゃあ 1 ファイルでやるには以下のようにするのが現実的かな。

<?php
require_once 'silex.phar';
$app = new Silex\Application();

$app->get('/', function () use ($app) {
    return 'Hello world';
});

if (getenv('SILEX_TEST')) {
    return $app;
}
$app->run();

で、テストコードの中で putenv('SILEX_TEST'); みたいな形にして実行してやる。

<?php
use Silex\WebTestCase;
putenv('SILEX_TEST'); 
class SampleTest extends WebTestCase
{
    public function createApplication()
    {
        return require dirname(__DIR__) . '/examples/index.php';
    }
    ...
}

$app; だけを応答する事が出来たので、2 つ目の問答無用に '/' が呼ばれるというのも解消された。

ドキュメントの間違い

WebTestCase を使用すると、定義したルーティング先のメソッドのテストが出来る。
で、ドキュメントに書いてある通りにやってみたが動かない。

For your WebTestCase, you will have to implement a createApplication method, which returns your application. It will probably look like this:

public function createApplication()
{
    return require __DIR__.'/path/to/app.php';
}

Make sure you do not use require_once here, as this method will be executed before every test.

http://silex-project.org/doc/testing.html#webtestcase

実行してみると…

Argument 1 passed to Symfony\Component\HttpKernel\Client::__construct() must implement interface Symfony\Component\HttpKernel\HttpKernelInterface, integer given, called in phar:///usr/local/lib/php/library/silex.phar/src/Silex/WebTestCase.php on line 15 and defined

こんな感じでテストが失敗するんじゃなくて例外が起きて実行できない。


で、他のエクステンションはどうやってやってんのかと思って調べてみたら、Symfony2 の Request を使っているというエクステンションだけで、WebTestCase を使ってるエクステンションを見つけられなかった。
しょうがないから、WebTestCase 自体のソースコードを読んだ。
Silex/WebTestCase.php at master · fabpot/Silex · GitHub


何の事はない abstract なメソッドを setUp() 時に呼んでいて、その戻り値をクラス変数に突っ込んでいる。
require だと読み込みが成功したか失敗したかしか返らないから、期待しているアプリケーションのインスタンスが取得できない。
もしドキュメントの様にアプリケーションのファイルを読み込むのなら、

<?php
public function createApplication()
{
-    return require __DIR__.'/path/to/app.php';
+    include __DIR__.'/path/to/app.php';
+    return $app;
}

が正しい。

'/' へ問答無用のディスパッチ

さて無事に WebTestCase を実行できるようになって、ごりごりテストコードを書いて実行したら以下のようなエラーが出た。

Symfony\Component\HttpKernel\Exception\NotFoundHttpException: No route found for "GET /"

確かに自分の書いたアプリケーションのコードには get('/') なルーティングは書いてない。
が、なんで問答無用に / へルーティングするのかが分からない。


どうやらアプリケーションで $app->run() を呼ぶと / へディスパッチすんかな?
ソースコードを追う気は全くなかったので、テスト用のアプリケーションにダミーの定義だけ書いて回避した。
アドホックな対応。