CarbonとCarbonImmutableの両方を組み合わせて使用するコードのテストでは両方のクラスでsetTestNow()を実行する必要がある
TL;DR
setTestNow()を実行する時は,現在の時刻を取得してるクラスでsetTestNow()を実行する必要がある
メソッドの戻り値がCarbonImmutableクラスでも内部でCarbonクラスを使っている場合はCarbonクラスでsetTestNow()を実行する必要がある
Laravel(というかilluminate/support)を使っている場合は,\Illuminate\Support\Carbon::setTestNow()を実行すると両方のクラスのsetTestNow()を実行してくれるので楽
Carbonで現在の時刻が絡むテストをするときはCarbon::setTestNow()を実行するのが一般的
setTestNow()の引数にCarbonのインスタンスや日時に変換できる文字列を渡すと現在の時刻を固定できる
code:php
var_dump(Carbon::now()->toDateTimeString()); // => "2023-05-27 17:37:49"
// 2023-05-01T00:00:00.000000+0900 にセット
Carbon::setTestNow(Carbon::createFromTimestamp(1682866800));
var_dump(Carbon::now()->toDateTimeString()); // => "2023-05-01 00:00:00"
CarbonクラスでもCarbonImmutableクラスでも同様
setTestNow()を実行しても固定されない場合がある
メソッドの戻り値が現在の時刻のCarbonImmutableを返すメソッドのテストを書くとする
Carbon::now()を呼んでイミュータブルに変換する関数とCarbonImmutable::now()を呼ぶメソッドを作った
どっちもCarbon::setTestNow()を実行してから,関数を呼んで戻り値のインスタンスのタイムスタンプと比較するだけのテスト
code:php
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use PHPUnit\Framework\TestCase;
function fn0() : CarbonImmutable
{
return Carbon::now()->toImmutable();
}
function fn1() : CarbonImmutable
{
return CarbonImmutable::now();
}
class ExampleTest extends TestCase
{
/**
* @test
*/
public function fn0_現在の時刻を取得できる() : void
{
$timestamp = 1682866800;
Carbon::setTestNow(Carbon::createFromTimestamp($timestamp));
$this->assertSame($timestamp, fn0()->getTimestamp());
}
/**
* @test
*/
public function fn1_現在の時刻を取得できる() : void
{
$timestamp = 1682866800;
Carbon::setTestNow(Carbon::createFromTimestamp($timestamp));
$this->assertSame($timestamp, fn1()->getTimestamp());
}
}
このテストを実行するとCarbonImmutable::now()を実行するfn1()の方のテストだけ落ちる
落ちる原因
Carbon::setTestNow()とCarbonImmutable::setTestNow()でセットした日時はそれぞれのクラスのみで適用される
Carbon::setTestNow()で時刻を固定するとCarbon::now()は固定されるが,CarbonImmutable::now()は固定されない
fn1の方のテストが通るようにするには,CarbonImmutable::setTestNow()を実行する必要がある
解決方法
利用する方のクラスでsetTestNow()を実行する
どっちを使うか分からない場合,両方のクラスでsetTestNow()を呼ぶ
両方呼んでおけば確実に時間を固定できる
code:php
$timestamp = 1682866800;
Carbon::setTestNow(Carbon::createFromTimestamp($timestamp));
CarbonImmutable::setTestNow(Carbon::createFromTimestamp($timestamp))
Laravelを使っている場合は\Illuminate\Support\Carbon::setTestNow()を使うと両方固定できる
illuminate/supportパッケージ内ではCarbonの拡張が定義されている
IlluminateのCarbon内にはsetTestNow()が定義されていて,これを呼ぶとCarbonとCarbonImmutableの両方でsetTestNow()を呼べる