Symfony2のコントローラーの単体テストへのアプローチに関する
Symfony2開発者の1人による昨日の投稿の翻訳に注目します。 このトピックは、Symfony2開発者に非常に関連しています。 また、Symfony2でコントローラーをサービスとして使用する
ことに関する開発グループに関する
議論の結果について投稿に言及していることも注目に値します。
MVCフレームワークの多くの経験があっても、常に解決されないことが1つあります。それは、コントローラーをテストする方法です。 コントローラーはフレームワークの「ブラックマジック」の要素に属しているため、この主な理由はテストの非自明性だと思います。 ファイルシステムでのコントローラーの配置、彼が知っておくべき依存関係、コントローラーに必要なハード接続(ビューレイヤー)に関して多くの合意があります。
このような状況は、コントローラーをテストするための簡単な方法を意味するものではありません。コントローラーと、相互作用をテストするための主要な依存関係を選択できるまでは、フレームワーク全体を実行して機能テストを実行する必要があります。
このプロセスは非常に複雑で複雑であるため、通常はコントローラーの単体テストに頼らず、機能テストを最大限に活用できますが、通常はテストはまったく行われません。
Symfony2は完全に変化しています。
当初、
Symfony2フレームワークには、コントローラーのロード契約のみがあります。 コントローラインスタンスは非常に軽量なままであり、その操作のために親クラスの拡張を必要としません。 コントローラーがContainerAwareインターフェースを実装している場合は、ContainerAware :: setContainer()メソッドを介して実装された
DIC (依存性注入コンテナー)を取得します。これを使用して、
DICで宣言したサービスにアクセスできます。
コントローラーをしばらくテストするための推奨される方法は、アプリケーションへの完全な要求をテストし、次のように出力を確認するときにブラックボックスをテストする近似です。
<?php
$client = $ this ->createClient();
$client->request( 'GET' , '/index' );
$response = $client->getResponse();
$ this ->assertEquals(200, $response->getStatusCode());
$ this ->assertRegExp( '/<h1>My Cool Website<\/h1>/' , $response->getContent());
この方法は読みやすく理解しやすいという事実にもかかわらず、欠点があります。
- テストを実行するには、カーネルを起動する必要があります。
- これは応答本文のみをテストするため、設計の変更に非常に敏感になります。
- 上記のすべての結果として、それははるかに遅く動作し、必要以上に多くを行います。
理想的な世界では、次のように、コントローラーとアプリケーション内の他のサービスとの相互作用をテストしたいと思います。
<?php
namespace Company\ApplicationBundle\Tests\Controller;
use Company\ApplicationBundle\Controller\IndexController;
class IndexControllerTest extends \PHPUnit_Framework_TestCase
{
//...
public function testIndexAction()
{
$templating = $ this ->getMock( 'Symfony\Bundle\FrameworkBundle\Templating\Engine' );
$templating->expects($ this ->once())
->method( 'render' )
->with( 'Application:Index:index' )
->will($ this ->returnValue( 'success' ))
;
$controller = new IndexController();
$controller->setTemplating($templating);
$ this ->assertEquals( 'success' , $controller->indexAction());
}
}
注:コントローラーは基本クラスのないPOPO(プレーンな古いPHPオブジェクト)になりました。これは拡張する必要があります。 Symfony2が機能するためには、コントローラークラス以外の機能を動作させる必要はありません。
注:
PHPUnitのモックオブジェクトの詳細をお読みください。
良いニュースは、Symfony2がこれを許可していることです。 これで、すべてのコントローラーがサービスとして機能できます。 過去の一般に受け入れられているバージョンもサポートされており、単体テストを必要としない小さなコントローラーには不可欠です。
上記の例のコントローラーがSymfony2と正しく対話し、正常に機能するためには、次のものが必要です。
コントローラクラスを作成します。
<?php
namespace Company\ApplicationBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Templating\Engine;
class IndexController
{
/**
* @var Symfony\Bundle\FrameworkBundle\Templating\Engine
*/
private $templating;
/**
* @param Symfony\Bundle\FrameworkBundle\Templating\Engine $templating
*/
public function setTemplating(Engine $templating)
{
$ this ->templating = $templating;
}
/**
* @return Symfony\Component\HttpFoundation\Response
*/
public function indexAction()
{
return $ this ->templating->render( 'ApplicationBundle:Index:index' );
}
}
次のxmlを使用してDIC構成を作成します。
<? xml version ="1.0" ? >
< container xmlns ="http://www.symfony-project.org/schema/dic/services"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation ="http://www.symfony-project.org/schema/dic/services www.symfony-project.org/schema/dic/services/services-1.0.xsd" >
< services >
< service id ="index_controller" class ="Company\ApplicationBundle\Controller\IndexController" >
< call method ="setTemplating" />
< argument type ="service" id ="templating" />
</ call >
</ service >
</ services >
</ container >
ルーティング構成を作成します。
<? xml version ="1.0" encoding ="UTF-8" ? >
< routes xmlns ="http://www.symfony-project.org/schema/routing"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation ="http://www.symfony-project.org/schema/routing www.symfony-project.org/schema/routing/routing-1.0.xsd" >
< route id ="index" pattern ="/index" >
< default key ="_controller" > index_controller:indexAction </ default >
</ route >
</ routes >
注:上記の例では、通常のBundleBundle:Controller:アクション(接尾部「Action」なし)の代わりにservice_id:アクションが使用されました。
これがすべて完了したら、Symfony2にサービスについて通知する必要があります。 Dependency Injection拡張機能が作成されず、構成ファイルのポイントが作成されるようにするには、サービスを直接登録できます。
<?php
namespace Company\ApplicationBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
class ApplicationBundle extends Bundle {
public function registerExtensions(ContainerBuilder $container) {
parent::registerExtensions($container);
// register controllers
$loader = new XmlFileLoader($container);
$loader->load(__DIR__. '/Resources/config/controllers.xml' );
}
}
注:上記の手法は、もともとOpenSkyでのプロジェクトの共同開発中に
Kris Wallsmithによって表明されました。
これですべて準備が整いました。 バンドルレベルのルーティングファイルをアプリケーションレベルのルーティングの構成に含め、インデックスディレクトリを作成する必要があります。 最終的なディレクトリ構造は、次のようになります。
Company
| - ApplicationBundle
| | - Controller
| | | - IndexController.php
| | - Resources
| | | - config
| | | | - controller_routing.xml
| | | | - controllers.xml
| | | - views
| | | | - Index
| | | | | - index.php
| | - ApplicationBundle.php
これらの手順を完了したら、ブラウザでURLを入力してこれを試すことができます。
your_application/your_front_controller.php/index