【Laravel】テストコード実行時に .env の値を参照したい場合、キャッシュの扱い方によって実行結果が変わる

computer-01 システム開発

【 環境 】
Laravel のバージョン: 8.61.1
PHP のバージョン: 8.0.16
MySQL のバージョン: 5.7.32

Laravel にてテストコードを実行する時、
「テスト実行前にコンフィグをキャッシュした時は “.env” から値を参照し、テスト実行前にコンフィグをキャッシュしなかった時は “phpunit.xml” から値を参照する」
という、知っておかないとドハマリする、なかなかに闇が深い挙動があったりする。

詳細は以下をご参照ください。
【Laravel】キャッシュクリア系コマンドには、テストコード実行前に流さない方がよいものがある | かきエンジン

例えば、以下のコード。

tests/Sample01Test.php

テストコードです。

    public function testPrueba01()
    {
        token = config('docurain.token');
        echo "===================================" . PHP_EOL;
        echo "token:{token}" . PHP_EOL;
        echo "===================================" . PHP_EOL;
    }

config/docurain.php

.env を読み込んでいるコンフィグファイルです。

return [
    'token' => env('DOCURAIN_TOKEN'),
];

.env

DOCURAIN_TOKEN はアクセストークンで、超適当にランダムで作成した値です。
「foo1234」といった雑な値にしてもよかったのですが、それだと以降の説明でイメージが付きづらい部分が出てきたので、それっぽい値を用意する事にしました。

DOCURAIN_TOKEN=FohVai3zei7dei8zainanuaphohqu5uz1Goh9aiy

phpunit.xml

    <php>
        <server name="APP_ENV" value="testing"/>
        <server name="BCRYPT_ROUNDS" value="4"/>
        <server name="CACHE_DRIVER" value="array"/>
        <server name="MAIL_MAILER" value="array"/>
        <server name="QUEUE_CONNECTION" value="sync"/>
        <server name="SESSION_DRIVER" value="array"/>
        <server name="TELESCOPE_ENABLED" value="false"/>
    </php>

上記のコードを実行する時、テストコード実行前にコンフィグをキャッシュするか、しないかで実行結果が変わってくる。

コンフィグをキャッシュする場合

実行コマンド

php artisan config:clear | php artisan config:cache
php artisan test tests/Sample01Test.php

実行結果

===================================
token:FohVai3zei7dei8zainanuaphohqu5uz1Goh9aiy
===================================

config/docurain.php にて定義した ‘token’ の値が取得できています。

コンフィグをキャッシュしない場合

実行コマンド

php artisan config:clear
php artisan test tests/Sample01Test.php

実行結果

===================================
token:
===================================

config/docurain.php にて定義した ‘token’ の値が取得できず、空白が出力されています。

このように、実行したソースコードは全く同じであるにもかかわらず、直前に実行したコマンドによって結果が変わってきます。

何が起こっているか

コンフィグをキャッシュする場合、.env 内の「DOCURAIN_TOKEN」の値を参照する。

コンフィグをキャッシュしない場合、phpunit.xml 内の 「server name=”DOCURAIN_TOKEN”」の値を参照する。
ただ、上記の場合では phpunit.xml にそんな値を設定していないので、空の値が出力されてしまう。

対策1. phpunit.xml に追記する。

例えば、こんな感じ。

    <php>
        <server name="DOCURAIN_TOKEN" value="FohVai3zei7dei8zainanuaphohqu5uz1Goh9aiy"/>
    </php>

見えようが見えまいがどうでもいい情報ならまだしも、アクセストークンとか超入れたくない。
そもそも、トークンが .env との二重管理になってしまい、その観点でも良い方法とは言い難い。

解決はできるものの、とんでもなく気持ち悪く、できれば他の方法を取りたいところだ。

他の案として、「テスト実行時は常にコンフィグをキャッシュし、.env の値をする」という方法がありはするが、テスト実行時に phpunit.xml に記述した内容を参照しないというのは、さすがにナシだろう。

対策2. テストコードの実行範囲を限定する。(CI/CD の対象から切り離す)

テストコードを CI/CD に組み込んで実行する場合、実行コマンドはこんな感じになるかと思います。

php artisan test

ユニットテスト、機能テストで分ける場合、こんな感じ。

php artisan test tests/Unit
php artisan test tests/Feature

phpunit.xml は、こんな感じ。

phpunit.xml

    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
    </testsuites>

この場合、テストコード実行の対象は、以下のディレクトリとなります。

/tests/Unit/*
/tests/Feature/*

.env から値を取得する必要があるものの、phpunit.xml にその値を書きたくない、といった情報(サービスへのアクセストークンなど)が必要なテストは、上記のディレクトリとは切り離した場所に保存する。例えば、こんな感じ。

/tests/Docurain/EstimateTestExtra.php

こういう事に気を回さないといけないケースは、おそらく外部サービスへのアクセスが必要で、その時にトークンやシークレットキーといった情報が必要になる時ぐらいかと思われます。

ただ、その場合、アクセス回数に制限があったり、アクセス数によって課金される金額が変わってきたりと、CI/CD に組み込むにはちょっと慎重にした方がいいケースが多いのではないかと思われます。

なので、CI/CD とは切り離し、その機能はテストを手動で実行、または通常のフローとは別の箇所で実行、と切り離した方がいいんじゃないかと思います。

追記

英語版も書いてみました。
https://dev.to/kakisoft/laravel-test-code-execution-results-depend-on-how-you-handle-the-cache-303

コメント

タイトルとURLをコピーしました