LoginSignup
29
34

More than 5 years have passed since last update.

Symfony2 + PHPUnitではじめてのテスト(機能テスト編)

Last updated at Posted at 2014-06-29

さっそくやってみよう

導入方法および単体テストについては、下記を参照ください。

Symfony2 + PHPUnitではじめてのテスト(単体テスト編)

機能テスト(Controller編)

テストケースの作成

コントローラーをconsoleから作成すると、テストケースももれなく作成されます。

Generating a New Controller - Symfony(current)

The generate:controller command generates a new Controller including actions, tests, templates and routing.

ControllerGenerator.php - Github

ControllerGenerator.php
$this->renderFile('controller/ControllerTest.php.twig', $dir.'/Tests/Controller/'.$controller.'ControllerTest.php', $parameters);

自動生成されたテストケースは下記のようなイメージ

<?php
namespace Sopra\WebBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class MypageControllerTest extends WebTestCase
{
}

環境変数を設定

必要に応じて、phpunit.xmlに変数設定を追加します。

phpunit.xml
<phpunit>
    <!-- ... -->
    <php>
        <server name="KERNEL_DIR" value="../app/" />
        <server name="SERVER_NAME" value="local.sopra.jp" />
        <server name="SERVER_PORT" value="80" />
        <server name="SCRIPT_NAME" value="" />
        <server name="REQUEST_URI" value="" />
    </php>
    <!-- ... -->
</phpunit>

テストメソッドの実装

大まかにいうとテストメソッドの処理フローは以下のようになります。

// 'test'で始まるpublic関数を定義
public function testExecuteSuccess() {

    // 必要に応じて環境変数を設定

    // 準備(データの初期化など)

    // ページにアクセス

    // テストしたい処理を実行(送信ボタンをクリックなど)

    // 結果を検証
}

実践してみよう

実際に実装したときに、つまづいた点などを中心にポイントを解説していきます。

ログイン

ログインが必要なページをテストする場合は、ログイン状態をシミュレートしなければいけません。
symfony.comに説明があるので、ほぼそのとおり実装しました。

How to simulate Authentication with a Token in a Functional Test (current) - Symfony

private function logIn()
{
    $session = $this->client->getContainer()->get('session');

    $firewall = 'secured_area';
    $token = new UsernamePasswordToken('admin', null, $firewall, array('ROLE_ADMIN'));
    $session->set('_security_'.$firewall, serialize($token));
    $session->save();

    $cookie = new Cookie($session->getName(), $session->getId());
    $this->client->getCookieJar()->set($cookie);
}

テスト用データの投入(Doctrin Fixtures Bundleの導入)

テストを実行するたびにデータを手動で投入していたのでは、意味が無いので、Doctrin Fixtures Bundleを導入することにしました。

DoctrineFixturesBundle - Symfony

composer.json
{
    "require": {
        "doctrine/doctrine-fixtures-bundle": "dev-master"
    }
}

いったんテーブルをtruncateしてからデータを投入するようにします。

EntityHelper.php
<?php

namespace Sopra\CoreBundle\DataFixtures\Helper;

use Doctrine\ORM\EntityManager;

class EntityHelper {

    private $entityManager;

    public function __construct(EntityManager $entityManager) {

        $this->entityManager = $entityManager;
    }

    public function truncateTables($tables) {

        $connection = $this->entityManager->getConnection();
        $platform = $connection->getDatabasePlatform();

        $connection->exec('SET FOREIGN_KEY_CHECKS = 0');

        foreach ($tables as $table) {
            $connection->exec($platform->getTruncateTableSQL($table));
        }

        $connection->exec('SET FOREIGN_KEY_CHECKS = 1');
    }

}

'SET FOREIGN_KEY_CHECKS = 0'は外部キーを無視するために実行していますが、mysql固有の関数なので修正必要ですね。

MySQL 5.1 リファレンスマニュアル :: 13.5.6.4 FOREIGN KEY 制約

さらに、テストケースからFixtureをloadするために、別のHelperクラスを実装しています。

FixturesHelper.php
<?php

namespace Sopra\CoreBundle\DataFixtures\Helper;

use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\ORM\EntityManager;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class FixturesHelper {

    private $container;
    private $entityManager;

    public function __construct(ContainerInterface $container, EntityManager $entityManager) {

        $this->container = $container;
        $this->entityManager = $entityManager;
    }

    public function loadFixtures($fixtures) {

        $loader = new Loader();
        foreach ($fixtures as $fixture) {
            if ($fixture instanceof ContainerAwareInterface) {
                $fixture->setContainer($this->container);
            }
            if ($fixture instanceof FixtureInterface) {
                $loader->addFixture($fixture);
            }
        }
        $executor = new ORMExecutor($this->entityManager);
        $executor->execute($loader->getFixtures(), true);
    }

}

各Fixtureからサービスコンテナにアクセスするために、ContainerAwareInterfaceを実装します。
sopraでは、EntityHelperに渡すEntityManagerを取得するために、setContainerしています。

Using the Container in the Fixtures - Symfony

各クラス、インターフェイスの関係をクラス図にまとめると下記のようになります。

data_fixtures.png

ページにアクセス

ページへのアクセスはSymfony\Bundle\FrameworkBundle\Clientオブジェクトを通じて行いますので、setupメソッドにて生成し、メンバ変数にセットしておきます。

BaseWebTestCase.php
<?php
namespace Sopra\WebBundle\Tests\TestCase;

use Symfony\Bundle\FrameworkBundle\Client;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

abstract class BaseWebTestCase extends WebTestCase {

    /** @var Client */
    private $client;

    protected function setUp() {

        parent::setUp();

        $this->client = static::createClient();

    }
}

Client::requestメソッドにてページにアクセスします。
リダイレクトされる場合はfollowRedirectし、戻り値のcrawlerオブジェクトを取得します。

$this->getClient()->request('GET', '/path/to/page');
$crawler = $this->getClient()->followRedirect();
/* @var $crawler Symfony\Component\DomCrawler\Crawler */

テストしたい処理を実行

'Submit'ボタンをクリックして、レスポンスを取得する場合は、下記のような実装になります。

$form = $crawler->selectButton('Submit')->form();

$this->getClient()->submit($form);
$crawler = $this->getClient()->followRedirect();

結果を検証

結果ページに'Success'という文字列が含まれているかどうかを確認するには、下記のような実装になります。

$this->assertTrue($crawler->filter('html:contains("Success")')->count() > 0);

'Success'と言う文字列が一つも含まれない場合は、その時点でテスト失敗となります。
上記のassertTrue意外にも、PHPUnitにはあらかじめ様々なアサーションメソッドが用意されています。

アサーション - phpunit

テスト実行

$ cd bin
$ ./phpunit -c ../app/ ../src/Sopra/WebBundle/Tests/Controller/SampleControllerTest.php
PHPUnit 3.7.27 by Sebastian Bergmann.

Configuration read from /path/to/Application Root/app/phpunit.xml

.....

Time: 7.95 seconds, Memory: 51.50Mb

OK (5 tests, 33 assertions)
29
34
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
34