When I look at the tests in a codebase, date logic usually lands in one of two buckets. Either none of it is tested, or all of it is, including things Laravel already handles.
The dates worth testing are the ones tied to your business rules. Expirations, deadlines, billing cycles, and time-based pricing are all important business rules, and they deserve a test.
Freeze time with Carbon::setTestNow() and check both sides of the boundary:
public function testSubscriptionExpiresAfterOneYear(): void
{
Carbon::setTestNow('2026-01-15');
$subscription = Subscription::factory()->create(['started_at' => now()]);
Carbon::setTestNow('2027-01-15');
self::assertTrue($subscription->isActive());
Carbon::setTestNow('2027-01-16');
self::assertFalse($subscription->isActive());
}
What isn't worth testing is Laravel's own date behavior.
You don't need a test proving created_at gets set when a model is saved.
The framework already has that covered.
Boundaries deserve extra attention, because Carbon doesn't always do what you'd guess. Adding a month to January 31 doesn't give you the end of February.
Carbon::parse('2026-01-31')->addMonth(); // 2026-03-03
Carbon::parse('2026-01-31')->addMonthNoOverflow(); // 2026-02-28
You don't need a test for addMonth() itself.
But if your billing logic calls it, a billing test for a customer who signed up on the 31st will find that surprise before your customers do.
One last thing to watch for.
Carbon::setTestNow() only affects Carbon and Laravel's now() helper.
If your code calls PHP's time() or date() directly, freezing time in the test won't change anything.
Test your rules, not Laravel's, and pay extra attention at the edges.
Here to help,
Joel
P.S. Our testing course has a whole lesson on testing date logic, from date validation rules to freezing time with Carbon::setTestNow().