👾
[備忘録]phpunitのdataProviderメソッドはsetupメソッドよりも先に呼び出されるっぽい
この記事について
- phpunitのテストコードを修正した際に遭遇した現象について、下手したらハマりそうだなと思ったので備忘録にしました
使用マシン/ツール
- macOS Sonoma ver14.7.5
- Docker Desktop ver4.23.0
- Laravel Framework ver10.32.1
- phpunit/phpunit ver10.4.2
経緯
- 業務でLaravelのタイムゾーンをJSTに変更することになったので、
config/app.php
のtimzezone="UTC"
をtimzezone="Asia/Tokyo"
に変更した - dataProviderでCarbonを生成し日時やタイムゾーンをチェックするphpunitテストでエラーが発生したので調査した
経緯/調査の詳細
- サンプルとして下記のようなテストコードを用意
※config/app.php
でtimzezone="Asia/Tokyo"
にしていることを前提にしています
use Illuminate\Support\Carbon;
use PHPUnit\Framework\Attributes\DataProvider;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
}
#[DataProvider('parameter')]
public function test_carbon(Carbon $checkDate): void
{
$actual = $checkDate->timezone;
$this->assertEquals('Asia/Tokyo', $actual);
}
public static function parameter(): array
{
return [
'タイムゾーンがAsia/Tokyo' => [Carbon::make('2025-05-01 00:00:00')]
];
}
想定では問題なくテストが通るはずだったが、下記エラーが発生
Failed asserting that Carbon\CarbonTimeZone Object &0000000000002ae30000000000000000 (
'timezone_type' => 3
'timezone' => 'UTC'
) matches expected 'Asia/Tokyo'.
-
test_carbon
内で$checkDate
をdumpで確認
^ Illuminate\Support\Carbon @1746144000^ {#333
~省略~
date: 2025-05-01 00:00:00.0 UTC (+00:00)
}
$checkDate
のタイムゾーンがUTCのままだった
- dataProviderで生成したCarbonがおかしいことがわかったので、今度は
parameter
メソッド内でdumpを仕込んでみたところ、test_carbon
メソッド よりも先にparameter
メソッド が呼び出されている ことがわかった
コード
#[DataProvider('parameter')]
public function test_carbon(Carbon $checkDate): void
{
dump($checkDate);
~省略~
}
public static function parameter(): array
{
dump('parameter:' . now()->timezone);
~省略~
}
実行結果
^ "parameter:UTC"
^ Illuminate\Support\Carbon @1746144000^ {#333
~省略~
date: 2025-05-01 00:00:00.0 UTC (+00:00)
}
- 思ってもない実行順だったので、
setup()
メソッドにもdumpを仕込んで実行順を確認してみた
コード
public function setUp(): void
{
dump('setup');
parent::setUp();
}
#[DataProvider('parameter')]
public function test_carbon(Carbon $checkDate): void
{
dump('test_carbon');
~省略~
}
public static function parameter(): array
{
dump('parameter');
~省略~
}
実行結果
^ "parameter"
^ "setup"
^ "test_carbon"
parameterメソッドがsetupメソッドより先に呼び出されていることがわかった
- もう少し深ぼってみた。
config/app.php
が読み込まれるタイミングがどの辺りなのかTests/TestCase.php
を追ってみたところ、CreatesApplication
Traitクラスをuseしており、中のcreateApplication
メソッドではbootstrap/app.php
を読み込んでいた。
試しに下記のようにコードに追記して実行してみた
public function createApplication(): Application
{
$app = require __DIR__.'/../bootstrap/app.php';
dump(config('app.timezone')); // ①
$app->make(Kernel::class)->bootstrap();
dump(config('app.timezone')); // ②
return $app;
}
すると下記エラーが発生
^ "parameter"
^ "setup"
FAILED Tests\Unit\ExampleTest > carbon with data set "check dateが5/1 0時" BindingResolutionException
Target class [config] does not exist.
①の段階ではbootstrapの読み込み前なのでエラーになってしまった。
①をコメントアウトして再実行すると、下記のようになった
^ "parameter"
^ "setup"
"Asia/Tokyo" ← ②のdump
"test_carbon"
configが反映されるのは setup
メソッドが実行されるタイミングと見てよさそう
対応について
今回はひとまず、dataProvider側で生成しているCarbon::makeにタイムゾーンを指定することで対応しました
Carbon::make('2025-05-01 00:00:00')->setTimezone('Asia/Tokyo')
まとめ
今回はたまたまdumpをこまめに仕込んでいたためすぐに気づけましたが、他にもconfigの設定が遅延して反映されることはありそうなので、dataProviderを使用する際には頭の片隅にでも覚えておくとよさそうです。
Discussion