jasonmccreary/double
Double is a Laravel package by Jason McCreary for creating test doubles. It lets you swap real services for lightweight, configurable fakes that record method calls and return stubbed responses, keeping your test suite fast and free of heavy mocking boilerplate.
Architecture fit. jasonmccreary/double is a test-double library, which places it entirely in require-dev — it never ships to production, never enters the request lifecycle, and adds zero bytes to the runtime artifact. That is the most favorable possible architecture profile for a third-party dependency: the blast radius of failure is your CI pipeline, not your users. Architecturally it competes with two incumbents already in the Laravel stack — PHPUnit's built-in createMock() and Mockery, which Laravel ships and its TestCase pre-wires. Double is therefore not filling a gap; it is a substitutive DX play. The correct framing for evaluation is "do we prefer its ergonomics enough to pay a switching cost," not "do we need this capability." The v1.0.1 patch does not change this positioning.
Integration feasibility. Mechanically trivial: one Composer line, no service provider, no config file, no published assets, no container bindings. It is a plain PHP library that works alongside PHPUnit and Pest. The real friction is not installation but surface area — mocking syntax appears in nearly every test file, so adoption is a hundreds-of-files decision disguised as a one-line decision. Coexistence with Mockery is technically viable (distinct namespaces, no global state collision expected), which makes a partial migration possible and de-risks a pilot.
Technical risk. Low in severity, moderate in lock-in. The library is a thin abstraction over test-only concerns, so a break manifests as red builds, not incidents. The material risks are: (1) single-maintainer concentration — this is one person's project, and a small star count indicates a small adopter base with correspondingly thin community support; v1.0.1 does show an external contributor (PR #21), which is a mild positive signal that the project is not purely solo-authored, though the maintainer/merge authority remains one person; (2) version-coupling to PHPUnit/Pest majors, since mocking libraries must track internals that PHPUnit changes freely between majors; (3) provenance — previously flagged as Repository: unknown, this is now resolved: the v1.0.1 changelog links github.com/jasonmccreary/double, confirming a canonical public repository with a PR-based contribution workflow and a tagged release history (v1.0.0 → v1.0.1). The supply-chain gate is cleared; the residual items are star count, bus factor, and the still-unvalidated PHPUnit/Pest support matrix.
What v1.0.1 changes. It is a single-line patch: "Fix passthru with inherited readonly properties." No new features, no deprecations, no breaking changes, no API surface change. Its evaluative value is threefold and modest: it (a) confirms the project is actively maintained post-1.0, (b) closes the provenance flag, and (c) partially answers one of the open technical questions below — readonly property handling is now exercised and, at least for the inherited-property path, working. That a readonly-related defect existed in 1.0.0 and was fixed within a patch cycle is itself informative: the abstraction has non-trivial edge cases around PHP 8.1+ semantics, and early adopters are effectively the test suite for those edges.
Key questions to put to the maintainer/team.
readonly properties now work. Still open: final classes/methods, enums, and union/intersection types, the cases where Mockery and PHPUnit diverge most painfully.$this->mock()/$this->partialMock(), container instance swaps, and queued-job/event fakes?Stack fit. Clean. A Laravel 10/11/12 codebase on PHP 8.2+ with Pest or PHPUnit is the target demographic, and the library imposes nothing on bootstrapping. No composer.json conflicts are expected beyond PHP/PHPUnit version floors, which should be checked against your current matrix. Verify that your lowest-supported PHP version is above the library's floor — the most common silent adoption blocker is a dev dependency quietly forcing a PHP upgrade in CI. Pin to ^1.0.1 or later; do not adopt 1.0.0, which carries the now-fixed readonly inheritance defect.
Migration path. Treat this as an additive pilot, not a replacement. Concretely: add to require-dev, introduce it in one or two low-traffic test directories, and leave Mockery and createMock() untouched everywhere else. Publish a short internal convention note — "new tests in these namespaces use Double; legacy tests are not to be rewritten opportunistically" — because unguided mixed adoption produces exactly the inconsistency you were trying to escape. Rewriting a large existing suite is almost never justified by DX gains; the ROI lives in new test authorship. A patch release of this size is not a migration trigger; it is a confirmation that migration is still viable.
Compatibility. Two concrete checks before merge: (1) a full suite run with the dependency installed but unused, to confirm no autoload or trait interference with Laravel's base TestCase; (2) a Pest/PHPUnit version pin review, since mocking libraries typically declare narrow PHPUnit constraints and a minor bump can strand you. Confirm behavior under your CI PHP matrix, not just local. Add a targeted test for inherited readonly properties if your domain models use them — that is the exact path v1.0.1 touched, and validating it in your codebase is cheaper than discovering a regression in a future upgrade.
Sequencing.
github.com/jasonmccreary/double. Remaining sub-step: confirm the Packagist entry and release tags resolve to that repo.Maintenance. A require-dev entry with no runtime footprint. Ongoing cost is Composer update hygiene plus a periodic check that the library tracks your PHPUnit/Pest major — the single most likely source of future breakage. Budget a small recurring line item for "mocking library compatibility" during framework upgrades; this is a real cost that Mockery currently absorbs for you as part of the Laravel upgrade treadmill. v1.0.1 is evidence the maintainer responds to correctness bugs, which is the maintenance signal that matters most for a dev dependency; it is not evidence of a support commitment.
Support. Still the weak point, marginally improved. Small adopter base, single maintainer, and no vendor behind it. There is no support contract, no SLA, and issue triage depends on one person's availability — though the presence of an external PR merged into v1.0.1 shows the contribution path is live. For a dev-only dependency this is tolerable — you can fork a mocking library of this size if you must — but it should be an explicit, documented accepted risk rather than an unexamined one. Confirm the license (MIT, per the record) permits the fork-and-maintain fallback.
Scaling. No runtime scaling dimension. The relevant scaling axis is team-wide and linear: cost grows with the number of engineers writing mocks, and payoff grows the same way. Beyond roughly a dozen engineers, inconsistent mocking idioms become a genuine review-friction tax, so if you adopt, adopt deliberately with a written standard rather than letting it spread organically.
Failure modes.
readonly inheritance bug illustrates the class of failure: a PHP 8.x semantic subtly mishandled, producing a wrong or failing mock rather than an obvious crash. Low severity, but it argues for validating the library against your actual model shapes during the pilot rather than trusting a clean install.Ramp-up. Low and asymmetric. Individual engineers pick up a DX-first mocking API in an afternoon — that is the whole thesis of the package. The hidden cost is unlearning: engineers fluent in Mockery will default to old patterns under deadline pressure, so pair the pilot with one internal demo and a couple of checked-in exemplar tests. Docs are thin (a README or small site), so internal conventions will carry most of the weight.
Recommendation posture. Unchanged: time-boxed pilot, not standardization. The v1.0.1 patch is a net positive but a small one — it clears the blocking provenance gate and demonstrates active maintenance, which means the pilot can now start without a supply-chain exception. It does not move the underlying economics: the downside is contained to CI, the upside is real but modest, and the switching cost is the dominant term. Proceed to the spike, gated on validating the PHPUnit/Pest compatibility matrix against your actual CI configuration.
Assessment reviewed against v1.0.1 (patch: fix passthru with inherited readonly properties). No breaking changes, no deprecations, no new features; prior recommendation retained with provenance flag cleared.
How can I help you explore Laravel packages today?