Stop Flaky Tests: Freeze Time in Laravel Testing
Source: Dev.to

The Problem
I had this test running smoothly on my machine, but on CI it failed randomly:
public function test_order_item_cancel(): void
{
$user = UserFixture::createUser();
$this->actingAsFrontendUser($user);
$order = OrderFixture::create($user);
$orderItem = OrderItemFactory::new()->for($order)->for($user)->create();
$response = $this->put(route('api-v2:order.order-items.cancel', ['uuid' => $orderItem->uuid]));
$response->assertNoContent();
$this->assertDatabaseHas(OrderItem::class, [
'uuid' => $orderItem->uuid,
'canceled_at' => Date::now(),
]);
}
Sometimes the test produced this error:
Failed asserting that a row in the table [order_items] matches the attributes {
"canceled_at": "2026-01-09T10:24:52.008406Z"
}.
Found: [
{
"canceled_at": "2026-01-09 12:24:51"
}
].
At first I just retried, but after reading The Flaky Test Chronicles VI I realized I needed to investigate whether this was a real bug or a flaky test.
Why This Happens
Date::now() is called twice:
- When the controller sets
canceled_at. - When the test checks the value.
Even a millisecond difference makes the timestamps unequal. CI environments are often slower, so the discrepancy appears more frequently there.
The Fix
Freeze time before making the request so both the controller and the test use the same timestamp.
// Option 1
$this->freezeTime();
// Option 2
$now = Date::now();
Date::setTestNow($now);
$response = $this->put(route('api-v2:order.order-items.cancel', ['uuid' => $orderItem->uuid]));
$this->assertDatabaseHas(OrderItem::class, [
'uuid' => $orderItem->uuid,
'canceled_at' => $now,
]);
$this->freezeTime() is a convenient wrapper around Date::setTestNow() that is scoped to the test lifecycle. With the time frozen, the timestamps match and the test becomes deterministic.
Another Way
If you only need to ensure the field is not empty, you can assert that canceled_at is not null:
$this->assertDatabaseMissing(OrderItem::class, [
'uuid' => $orderItem->uuid,
'canceled_at' => null,
]);
Final Thoughts
When tests depend on time, take control of it. If a test passes locally but fails in CI, freeze time using Date::setTestNow() or $this->freezeTime(). Making your tests deterministic will keep them reliable and trustworthy.