Symfony Advent Calendar 2016 19日目の記事です。
はじめに
Symfony2でアプリケーションを開発している人たちの多くは、アプリケーションのファクショナルテストを書いているかと思いますが、 今回はアプリケーションのファンクショナルテストではなくて、バンドルのファンクショナルテストについて書きたいと思います。
バンドル単体のファンクショナルテストがない場合の問題
バンドルはKernel
にインストールされて使用されます。
バンドルをファンクショナルテストしないままリリースしてしまうと、アプリケーションのKernel
にインストールされてから問題に直面する可能性が上がってしまいます。
- DIコンテナのコンパイルに失敗する
- Deprecation Warningが出てしまう
- 期待した通りに動かない
- などなど・・・
問題は修正してしまえば良いのですが、Symfonyの文化圏内だとそう簡単には後方互換を捨てられません。
後方互換を維持したまま修正できれば何の問題もないです。もしそうでない場合はメジャーバージョンを上げるべきでしょうが、そうホイホイ上げたくありませんよね。
問題1: DIコンテナのコンパイルに失敗する
OSSで提供されているバンドルの多くは、Symfonyの複数のバージョンをサポートしています。
Symfonyはv2.3
から後方互換性を維持してくれている ので、そこまで神経質にならなくても大丈夫ですが、メジャーバージョンを跨いで複数のSymfonyをサポートしていると、バンドルをアプリケーションのKernel
にインストールした結果、DIコンテナのコンパイルに失敗するようになってしまう可能性は低くありません。
メジャーバージョンが変更される際に、削除が予定されているサービスは削除されるので、もし参照が残っていたりするとこれに該当します。
問題2: Deprecation Warningが出てしまう
Symfonyのアップグレードをスムーズに行わせる仕組みの1つにDeprecation Warning
を出力する仕組みがあります。さらに、テスト結果からDeprecation Warning
を確認できる仕組みも提供されています。
なのでSymfonyユーザーはDeprecation Warning
に敏感です。
あるバージョンのSymfonyでバンドルを利用しているユーザーは大丈夫だけれども、より新しいバージョンのSymfonyを利用しているユーザーだとそうではない場合があります。
あるバージョンで非推奨となったAPIや、サービスを実行したり参照したりするとこれに該当します。
問題3: 期待した通りに動かない
Symfonyはプラガブルにフレームワークの振る舞いを変更する事ができ、もちろんアプリケーションだけでなくバンドルからも変更する事が可能ですが、それらはバンドル自身の機能ではありません。
例えばEventDispatcher
はSymfonyのコアに組み込まれていますが、サービスとしてはFrameworkBundle
が提供していますし、Kernel
のイベントもHttp
コンポーネントが発行しています。
つまり、フレームワークの振る舞いを変更する機能がバンドル内にある場合、ユニットテストだけでは想定通りのタイミングで動作するのか、想定通りな値を受け取れるのか、想定通りに振る舞いを変更できているのか、そもそも動作するのか、何も保証できないという事です。アプリケーションの場合と同じですね。
どうやってファンクショナルテストするか
手順は基本的にアプリケーションの時と同じです。
- テスト用の
Kernel
を作る - テスト用の
Kernel
にバンドルをインストールする - テスト用の設定をテスト用の
Kernel
にロードさせる - テスト用の
Kernel
でファンクショナルテストする
テスト用のKernel
を作る
普段のアプリケーションと同じように、Symfony\Component\HttpKernel\Kernel
を継承したKernel
を作ります。
<?php
use Symfony\Component\HttpKernel\Kernel;
class TestKernel extends Kernel
{
}
テスト用のKernel
にバンドルをインストールする
テストしたいバンドルと、普段のアプリケーションと同じようにFrameworkBundle
もインストールします。
<?php
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
class TestKernel extends Kernel
{
+ public function registerBundles()
+ {
+ return [
+ new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
+ new Your\Bundle\YourSpecialBundle(),
+ ];
+ }
}
テスト用の設定をテスト用のKernel
にロードさせる
どの設定ファイルをロードするか指定します。(もちろんテストしたい設定も追加してください)
<?php
+use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\HttpKernel\Kernel;
class TestKernel extends Kernel
{
public function registerBundles()
{
return [
new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Your\Bundle\YourSpecialBundle(),
];
}
+ public function registerContainerConfiguration(LoaderInterface $loader)
+ {
+ $loader->load(__DIR__.'/config.yml');
+ }
}
テスト用のKernel
でファンクショナルテストする
Symfonyが提供しているファンクショナルテスト用のクラスSymfony\Bundle\FrameworkBundle\Test\WebTestCase
がありますが、そのクラスにはテストに使うKernel
を指定する事ができます。
毎回Kernel
を指定しても問題ないですが手間なのでファンクショナルテスト用のサブクラスを作っておくと便利です。
<?php
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase as BaseWebTestCase;
class WebTestCase extends BaseWebTestCase
{
protected static function getKernelClass()
{
return TestKernel::class;
}
}
あとはいつも通りの手順でバンドルのファンクショナルテストができます。
<?php
class YourFunctionalTest extends WebTestCase
{
public function testFeature()
{
$client = self::createClient();
$client->request('GET', '/path/to/feature');
}
}
複数バージョンのSymfonyでファンクショナルテストする
特定のSymfonyだけでなく、サポートする全てのバージョンのSymfonyでテストできればより良いですよね。 そんな時に Travis CI を使えば、複数の環境を1度にテストできて大変捗るのでオススメです。
Travis CI には Build Matrix 機能があり、下図のように全てのバージョンのSymfonyにインストールされた状態を、全てのPHPバージョンでテストする事ができ、殆ど完璧な状態を保つ事が簡単にできます。
PHP 5.5 | PHP 7.0 | |
---|---|---|
Symfony v2.8.x | Symfony v2.8.x - PHP 5.5 | Symfony v2.8.x - PHP 7.0 |
Symfony v3.1.x | Symfony v3.1.x - PHP 5.5 | Symfony v3.1.x - PHP 7.0 |
Symfony v3.2.x | Symfony v3.2.x - PHP 5.5 | Symfony v3.2.x - PHP 7.0 |
どのように設定するかはSymfony本家やFriendsOfSymfonyのリポジトリが大変参考になります。
まとめ
基本的にはバンドルもアプリケーションと同じ方法でファンクショナルテストができますが、Symfony Standard Edition と違い、初めからファイルが準備されていないので少しだけ準備が必要だというだけでした。本当によくできていて感心します。