1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PHPで書いたレガシーコードをテスト可能にする

Posted at

はじめに

久々にPHPの酷いコードにテストを足したのでまとめるよ。

患者さん事例

PHPって地の文ベタ書きで全然かけてしまう。
なので雑に書いたコードが適当なフォルダに突っ込まれていて、それをApatchが直接参照していたりするわけだ。
拡張されまくったり炎上したりで結果的にとんでもなく酷いコードが出来上がる。
そう、こんな感じに。

index.php
<?php
require_once "External.php"; // global変数定義

// おもむろに追加された認証ロジック

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    // 業務ロジック
    echo "<html>";
    if (/* なんかややこしい判定 */) {
         echo "へんなとこでボディを返したり";
         if (/* if文の深淵で */){ exit; } // なんてされてたら目も当てられない
    }
    else{
         header("クレイジーなところでヘッダを返していたりする");
    }
    echo "</html>";
}
else {
    // 業務ロジック
    echo "<html>どうのこうの</html>";
}

テストコードを加えたいところだがこのままでは無理だ。
なので最低限の修正でテスト可能にしておこう。

地の文いきなり走り出す問題の対処

最悪なことに地の文は読み込まれても実行されてしまう。だからまずはそれを封じる必要がある。

IndexTest.php
<?php
require_once "product code"; // ここでプロダクトコードが動いてしまう!

class IndexTest extends TestCase
{
    public function testPostWithNoParameter()
    {
        /* ここで実行したいのに! */
    }
}

これはもうなんともできんのでプロダクトコードに変更を加える。

index.php
<?php
require_once "External.php"; // global変数定義

function run($globalVarA, $globalVarB)
{
    // 元あった地の文
}

if (http_response_code() !== false) {
    run($globalVarA, $globalVarB);
}

末尾のIF判定のおかげでUT実行時は run は実行されない。
(もっといいアイデアがあればよこしやがってください)

注意したいのはグローバル変数で、それだけで死刑に相当するのだが今は粛清より修正だ。
こればっかりは埋まっているのを探し当てるしか無い。最もリスクのある部分。

これで一旦画面ポチポチしなくてもUTから run をけるだけでテストできるようになった。

echo/headerの追放

コイツラはapatch側に処理を返しているデータだ。
だからUTでこの値をチェックしたい。なので run の返り値にしてやろうじゃないか。

index.php
<?php
require_once "External.php"; // global変数定義

class HttpResponse
{
    // なんかいい感じに定義
};

function run($globalVarA, $globalVarB)
{
    $headers = [];
    $contents = [];

    // 元あった地の文
    //    header(xxx); => $headers[] .= xxx;
    //    echo xxx; => $contents .= xxx;

    return new HttpResponse($headers, $contents);
}

if (http_response_code() !== false) {
    $httpResponse = run($globalVarA, $globalVarB);
    foreach($httpResponse->headers as $header) {
        header($header);
    }
    echo $httpResponse->body();
}

サーバから渡ってくる値も変えたいな

サーバから渡ってくる色んな値を想定してテストしたいよね?
だから引数で渡せるようにしよう。

index.php
<?php
require_once "External.php"; // global変数定義

class HttpResponse
{
    // なんかいい感じに定義
};

function run(
    $globalVarA,
    $globalVarB,
    $requestMethod,
    $requestParameter,
    /* 他にあればどうぞ */
) {
    $headers = [];
    $contents = [];

    // 元あった地の文
    //    header(xxx); --->  $headers[] .= xxx;
    //    echo xxx; ---> $contents .= xxx;

    return new HttpResponse($headers, $contents);
}

if (http_response_code() !== false) {
    $httpResponse = run(
        $globalVarA,
        $globalVarB,
        $_SERVER['REQUEST_METHOD'],
        $_RESUEST,
        /* 他にあればどうぞ */
    );
    foreach($httpResponse->headers as $header) {
        header($header);
    }
    echo $httpResponse->body();
}

まぁだいたいこんな感じです。

その他

  • run関数をクラス化したかったらする(自分は関数でいいと思う)
  • CookieやSessionの情報もちゃんと切り出す
1
2
1

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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?