読者です 読者をやめる 読者になる 読者になる

Symfony Best Practice 訳してみた - Chapter11 -

Symfony Best Practiceの翻訳をしてます。 9日目−3です。これでBestPracticeは最終章です。

前回の記事

hanahirodev.hatenablog.com

※多分に意訳が入っておりますので、誤訳がある場合はご指摘ください。

※読み進めていくSymfony Best Practiceは 2016/10/23時点の情報です。

※写経をする場合は、事前にPHPの実行ができる環境をご用意ください。

3行まとめ

  • ファンクショナルテストを飛ばすことなかれ。

  • 簡単でもいいからファンクショナルテストを書こう。必ず将来役に立つ。

  • ファンクショナルテストではURLをハードコーディングしよう。


第11章 テスト

大雑把に言えば、テストには2種類あります。ユニットテストでは、ある機能の入出力をテストできます。ファンクショナルテストでは「ブラウザ」から操作(リンクのクリックやフォームへの入力とそのチェック)した結果を判定することができます。

ユニットテスト

ユニットテストは、Symfonyからは独立して存在するべき、「ビジネスロジック」をテストするものです。このため、Symfonyユニットテストのためにどういったツールを使うかといった制約を持っていません。最もポピュラーなものは、PhpUnit*1PhpSpec*2です。

ファンクショナルテスト

良いファンクショナルテストを書くのは大変なので、テストをしていない開発者もいます。ファンクショナルテストを飛ばしてはいけません!簡単なファンクショナルテストを書くことで、デプロイする前に致命的なエラーを発見できるのです。

  • 最低でも作ったページがきちんとロードされるかを確認するファンクショナルテストを書きましょう。

ファンクショナルテストはこんなに簡単です。

// tests/AppBundle/ApplicationAvailabilityFunctionalTest.php
namespace Tests\AppBundle;

use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;

class ApplicationAvailabilityFunctionalTest extends WebTestCase
{
    /**
     * @dataProvider urlProvider
     */
    public function testPageIsSuccessful($url)
    {
        $client = self::createClient();
        $client->request('GET', $url);

        $this->assertTrue($client->getResponse()->isSuccessful());
    }

    public function urlProvider()
    {
        return array(
            array('/'),
            array('/posts'),
            array('/post/fixture-post-1'),
            array('/blog/category/fixture-category'),
            array('/archives'),
            // ...
        );
    }
}

このテストでは、HTTPレスポンスが200または299であることを確かめることで、URLのロードが成功したか判定しています。ここだけ見ると意味のあることには思えないかもしれませんが、テストを書くのにかかる労力に比べれば、アプリケーションにファンクショナルテストがあることには価値があります。 コンピューターソフトウェアにおいて、こうしたテストはスモークテスト*3と呼ばれ、将来のリリースに備えて、単純なミスを発見する予備的なテストで構成されます。

ファンクショナルテストにURLをハードコーディングする

先ほどのファンクショナルテストでURLジェネレーターを使わないことに疑問を持った方がいるでしょう。

  • ファンクショナルテストで使うURLはURLジェネレーターを使うのではなく、ハードコーディングすべし。

テストするページのURL生成にrouterサービスを使った以下の例で考えてみましょう。

public function testBlogArchives()
{
    $client = self::createClient();
    $url = $client->getContainer()->get('router')->generate('blog_archives');
    $client->request('GET', $url);

    // ...
}

これは動きますが、1つだけとんでもない欠点があります。開発者が誤って、blog_archivesのルーティングを変えてしまったとしましょう。テストは通りますが、もともと(修正前の)のURLでは動かなくなってしまいます!そのURLに付けられたブックマークは機能しなくなり、検索エンジンのランキングも落ちてしまいます。

JavaScriptのファンクショナルテスト

標準搭載のテストクライアントは素晴らしいですが、JavaScriptの挙動をテストすることはできません。もしテストする必要があるなら、PHPUnitに搭載されているMink*4というライブラリを利用してみてはいかがでしょう。 もちろん、巨大なJavaScriptによるフロントエンドをお持ちなら、JavaScriptベースのテストツールを使った方が良いでしょう。

ファンクショナルテストについてさらに詳しく

HautelookAliceBundle*5はFaker*6とAlice*7を使って、データを作り実運用に近い状態でテストすると良いでしょう。


感想

「意味のある」ファンクショナルテストは絶対に書こう。 ルーティングと「検索エンジンのランキング」の関係は確かに盲点だった。