やみとものプログラミング日記 やみとものプログラミング日記
TOP テスト駆動開発でルーターを作る Part3
テスト駆動開発でルーターを作る Part3

テスト駆動開発でルーターを作る Part3

プログラミング
作成日時: 2020年7月8日
更新日時: 2020年7月9日
こんにちは、やみともです。
今回は前回「テスト駆動開発でルーターを作る Part2」の続きです。

前回までで最初のテストケースを実装し、テストを通すところまで実装しました。
今回はルーターの機能部分を作っていこうと思います。
ポイントは「実装する前にテストを書く」です。

not 実装 but テスト

まずは実装・・・ではなく、テストを書きましょう。
前回はIndexページ(トップページ)のコントローラークラス名を返す実装を行いましたが、今回は問い合わせページのコントローラークラス名Contactを正しく返すように実装・・・、ではなくテストを書きましょう。

[ RouterTest.php ]
class RouterTest extends TestCase
{
    public function test_routing_index()
    {
        Router::get("/", "Index");
        $controller_class_name = Router::routing("get", "/");
        $this->assertEquals("Index", $controller_class_name);
    }

    // ★追加
    public function test_routing_contact()
    {
        Router::get("/contact", "Contact");
        $controller_class_name = Router::routing("get", "/contact");
        $this->assertEquals("Contact", $controller_class_name);
    }
}

テスト結果

$ p
PHPUnit 9.2.5 by Sebastian Bergmann and contributors.

.F                                                                  2 / 2 (100%)

Time: 00:00.019, Memory: 4.00 MB

There was 1 failure:

1) RouterTest::test_routing_contact
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Contact'
+'Index'

/Users/yuki/testblog/yamitomo_router/tests/RouterTest.php:20

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

はい。テストが失敗しました。
まずは新しい機能を実装する前にテストを失敗させる ← 多分これが大事です

実装のための考察

ここで2つ目のテストを通す最短実装は、Routerクラスのrouting()メソッドの返り値をContactにすることだと思います。しかしそうすると当たり前ですが、1つ目のテストが失敗するようになります。

そうならないために、ここでは少し先まで実装をしてみようと思います。
僕の実装は次のようになりました。

実装

[ Router.php ]
class Router
{
    private static $routing_table = [];

    public static function get(string $url, string $controller)
    {
        // ルーティングテーブルにルートを登録する
        array_push(self::$routing_table, [
            "url" => $url,
            "controller" => $controller
        ]);
    }

    public static function routing(string $method, string $url)
    {
        // ルーティングテーブルのルートを1つずつ見ていきマッチしたコントローラークラス名を返す
        for ($i = 0; $i < count(self::$routing_table); $i++) {
            $route = self::$routing_table[$i];
            $url_pat = "#^{$route['url']}$#";
            $controller = $route["controller"];

            if (preg_match($url_pat, $url)) {
                return $controller;
            }
        }
    }
}

foreachではなくforを使っているのは僕の主義です。無視してください。
その他の部分について簡単に説明します。

まずはRouter::get()メソッドでは、前回までは何もしていませんでした。
引数の$urlと$controllerをちゃんと受け取るようにし、Routerのスタティック変数$routing_tableにルート情報を登録するようにしました。

次にRouter::routing()メソッドですが、これも引数$methodと$urlを受け取るように変更し、$routing_tableのルート情報を1つずつ確認して、マッチしたルートのコントローラークラス名を返すようにしました。

テスト Passed

以上の実装を行いテストが通りました。
$ p
PHPUnit 9.2.5 by Sebastian Bergmann and contributors.

..                                                                  2 / 2 (100%)

Time: 00:00.020, Memory: 4.00 MB

OK (2 tests, 2 assertions)

次回予告 & 本紹介

次回以降もルーターライブラリを作っていきます。
ちなみにこれまでの説明もこれからの説明も下のテスト駆動開発の本を参考にしています。
テスト駆動開発に興味のある方は購入されてはどうでしょうか。