Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Pest Plugin Test Time Laravel Package

spatie/pest-plugin-test-time

A Pest plugin for controlling time in tests. Freeze or advance Carbon’s current time with testTime()->freeze() and addMinute(), and use the toBeCarbon expectation to assert Carbon date and time values clearly.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. This is a test-only dev dependency with no production surface: no service provider, no config, no facade, no published assets, no migrations. It is a Composer plugin that Pest auto-discovers, and it is a thin wrapper over Carbon::setTestNow(). Architecturally it touches exactly one boundary — the test harness — and nothing else. Fit is clean but narrow: it assumes Laravel-family conventions (Carbon as the time source) and assumes Pest. In a PHPUnit suite, the package delivers zero value.

Integration feasibility. This is close to the lowest-effort integration a TPM will ever sponsor: one composer require --dev line, no CI pipeline changes beyond the existing Pest run, no environment configuration, no secrets, no infra. Realistic effort is 1–2 hours to pilot on one suite and write a short house-style note. There is no meaningful proof-of-concept phase required; the risk is not "will it install" but "will the team use it consistently."

Technical risk. Four risks matter, ranked:

  1. Global process state. setTestNow() mutates process-wide clock state. Without a defensive afterEach(fn () => Carbon::setTestNow()), one frozen test leaks into the next. The signature symptom is the worst kind of failure for a PM to reason about: a test passes in isolation and fails in the full suite, intermittently, depending on ordering.
  2. Time-source mismatch / false confidence. Frozen Carbon time does not affect time(), mktime(), SQL NOW()/CURRENT_TIMESTAMP, or Carbon::createFromTimestamp(time()). Any domain logic or raw query reading the native clock or the database will silently diverge from the frozen value. The test goes green while asserting something the system does not actually do — a correctness risk wearing the costume of a quality improvement.
  3. Version coupling to the test framework. The matrix is unforgiving: 2.3.0 targets Pest 5, requires PHP 8.4, and dropped Carbon 2; 2.2.x is the Pest 2/3 line. Adopting this package means your date-testing convention is now a passenger on your Pest/PHP upgrade cadence. That is the single decision-relevant coupling here.
  4. Thin adoption signal. 51 stars, 0 dependents. There is no ecosystem backstop. Mitigating factor: the package is small, MIT, and Spatie-maintained, and the entire API is testTime() plus one expectation, so the exit cost is a find-and-replace back to Carbon::setTestNow(). This is vendorable-in-an-afternoon risk, not platform risk.

Competitive reality to weigh. If the codebase is on Laravel, the framework already ships freezeTime(), travelTo(), travel()->days(), and travelBack(). The marginal value of this package over what you already own is therefore (a) terser Pest-closure ergonomics and (b) the toBeCarbon expectation, which is genuinely the most durable differentiator for review readability. A TPM should frame adoption as an ergonomics decision, not a capability decision.

Key questions to resolve before committing:

  • Are we a Pest shop, or PHPUnit, or both (and if both, does a Pest-only convention create a two-tier test culture)?
  • Is our Pest major 2/3 (pin 2.2.x) or 5 (requires PHP 8.4, Carbon 3)? What is our PHP floor today, and when do we intend to move it?
  • Does our domain code read "now" exclusively through Carbon/now(), or do raw SQL NOW() and native time() appear in date-sensitive paths?
  • Do we already have a house style built on Laravel's travel* helpers? Standardizing on a second idiom is a net negative.
  • Who owns test infrastructure and will own the convention doc and the reset guardrail?

Integration Approach

Stack fit. Laravel (or any PHP project using Carbon) + Pest. Works with CarbonImmutable. Does not apply to PHPUnit-only suites, non-Carbon date libraries, or teams without a Pest migration on the roadmap. If the org is mid-migration from PHPUnit to Pest, this package is correctly sequenced after that migration completes, not before.

Migration path. None required — the integration is purely additive. No existing tests change behavior. The exit path is equally clean: remove the dependency and replace testTime()->freeze(...) / add* / sub* calls with direct Carbon::setTestNow() invocations, and inline the toBeCarbon assertions. Documenting that exit path up front is worth ten minutes of a TPM's time because it converts "new dependency" into "reversible tooling choice," which changes how risk-averse reviewers receive it.

Compatibility. Verify and record three coordinates before rollout: Pest major, PHP floor, Carbon major. Treat the version pin as a deliberate decision in the PR, not an incidental one. If mixed Pest versions exist across repositories, expect the pin to differ per repo and standardize the convention, not the version number.

Sequencing.

  1. Pilot on one suite that is already known-flaky or date-dependent — ideally a trial/subscription-expiry or proration rule the team genuinely distrusts.
  2. Land the afterEach reset in Pest.php in the same PR as the first use. Do not defer this.
  3. Add two or three named time helpers to Pest.php for recurring domain moments (renewal window, month-end, leap day) so the concept has one definition.
  4. Publish a half-page house style: freeze at boundaries, hop forward in literal domain steps, never compute expected values with now() inside the assertion, always pass an explicit format to toBeCarbon in shared code.
  5. Only then mention it to other teams — as available, not mandated.
  6. Optional guardrail: run the suite in randomized order in CI to surface state leaks early, and add a lint rule or review checklist item for freeze-without-reset.

Operational Impact

Maintenance. Effectively nil at runtime — there is no production artifact, so no on-call exposure, no perf surface, no security patch urgency beyond routine dependency hygiene. The real maintenance events are annual-ish: a Pest major bump, a PHP floor bump, a Carbon major bump. Renovate/Dependabot will handle routine version churn; the Pest 5 / PHP 8.4 / Carbon 3 transition is the one that will require a deliberate ticket because it forces an infrastructure decision the package cannot make for you.

Support. MIT, community-maintained Spatie OSS. No SLA, no paid support tier for this package, no dependents to indicate ecosystem pressure to fix issues. Internal ownership should sit with whoever owns test infrastructure — not distributed across application teams, who will each invent a slightly different reset pattern.

Scaling. Technically the package scales without limit. The scaling problem is convention scaling: N teams, N flavours of freeze/reset/assert. Left unmanaged you get suites that are individually readable and collectively inconsistent, plus occasional cross-file flakiness that erodes trust in the test suite as a whole. The mitigation is cheap (one helper file, one paragraph of documented style) but it must be owned by someone.

Failure modes.

  • State leak — frozen clock survives into the next test; intermittent, ordering-dependent failures. Highest-frequency failure; mitigated by the afterEach reset and randomized-order CI.
  • False-positive date assertions — DB NOW() or native time() diverges from frozen Carbon; tests pass while behavior is unverified. Highest-severity failure, because it is invisible.
  • Upgrade breakage — a Pest 5 adoption pulls a PHP 8.4 floor and Carbon 3 requirement into the test stack, potentially ahead of the application's own readiness.
  • Idiom fragmentation — travel() and testTime() used interchangeably across repos, doubling the cognitive load for anyone moving between codebases.
  • Dead weight — teams on PHPUnit inherit a dependency that does nothing for them.

Ramp-up. Under an hour for a competent Laravel developer. The learning is not the API — it is the setTestNow() mental model and its limits (what it does not freeze). Budget the ramp-up time on the limits, not the syntax. That is where the correctness risk lives, and it is the only part of onboarding that a TPM should insist is documented.

Verdict. Recommend as an optional, opt-in convenience for Pest-based Laravel repositories, with adoption concentrated on date-heavy domains where the ergonomics and toBeCarbon readability pay for themselves. Do not mandate it org-wide, do not treat it as a testing-quality initiative, and do not let it become the answer to "our date tests are flaky" — the reset convention and the time-source audit are the answers, and they are necessary with or without this package.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin