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

Phpunit Service Create Trait Laravel Package

pkly/phpunit-service-create-trait

A PHPUnit 12+ trait (PHP 8.4+) that builds real service instances with auto-mocked constructor and #[Required] dependencies. Dependencies stay as invisible stubs until fetched, so you only get mock verification where you ask for it.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Upgrade sequencing for PHP 8.4 / PHPUnit 12: This package only exists on PHPUnit 12–13 and PHP 8.4+. It's not a reason to upgrade on its own, but it's a concrete, low-cost payoff to attach to an otherwise "compliance-driven" toolchain upgrade — a tangible DX win engineers can point at instead of a pure risk item. If the roadmap already contains a PHPUnit upgrade, this is a candidate to bundle into that workstream's acceptance criteria.
  • Test-suite maintenance cost (a build-vs-buy call at micro scale): Every service signature change today forces edits across its test file(s). This trait collapses that to one line — $this->createRealMockedServiceInstance(OrderService::class) — and keeps tests compiling when a constructor gains or reorders a dependency. The PM-relevant decision is not "adopt a package," it's "do we keep paying a per-refactor test-maintenance tax, or spend a day eliminating it?" Either way the work is small; the decision is cheap to make and cheap to reverse.
  • Refactor and modernization velocity: Large-scale service refactors (extracting repositories, splitting god-services, introducing interfaces) are gated by how much test churn they cause. Anything that makes test files resistant to constructor churn lowers the cost of the refactors already on the roadmap, which is often the real bottleneck rather than the production change.
  • Standardization of unit-test conventions: The strongest product idea here isn't auto-wiring (that's mechanical) — it's "stubs by default, mocks on demand." Unfetched doubles stay invisible to PHPUnit; getMockedService() registers an expectation so it's verified; getStubbedService() returns a plain stub. That is an opinionated answer to a real quality problem: teams either drown in "no expectations configured" noise (and learn to ignore PHPUnit output, which is how real failures get missed) or over-mock every dependency and produce brittle, assert-nothing tests. Adopting this trait is partly adopting a testing philosophy, so it's a fit question, not just a utility question.
  • Where it plausibly earns its keep: constructor-heavy Laravel service classes (OrderService → repository + payment client + logger), services that also use Symfony-style #[Required] setter injection, and legacy services whose constructors are wide and volatile.
  • Where it does not move the needle: thin controllers, HTTP/feature tests, Eloquent/model-heavy tests, and anything driven by Laravel's container or Http::fake()/Queue::fake(). For those, this trait is irrelevant — don't let it into a "test quality initiative" narrative where it doesn't belong.
  • Risk posture is the deciding factor, not capability. Zero stars, zero dependents, a single author, and a README-plus-releases maturity level mean this is a low-value, non-zero-risk dependency. The realistic alternatives are: adopt as-is, vendor a fork (~a few hundred lines around Reflection), or reimplement internally. Because the mechanism is simple, "buy" and "build" are close in cost; what you're really buying is the author's handling of edge cases (default params, scalars, enums, nullable deps, duplicate dependency types, internal classes) — that's the part worth paying for and the part worth auditing before you rely on it.

When to Consider This Package

Adopt (as a pilot, scoped to one module) if:

  • You are already on, or committed to, PHP 8.4+ and PHPUnit 12+. On anything older this is a non-starter — the previous assessment's "PHPUnit 10+" framing is wrong and would waste engineering time.
  • Your service layer has wide or churning constructors, and test files are being edited mainly to satisfy constructor signatures rather than to change test intent.
  • You want consistent, low-noise mocking semantics across teams and are willing to codify "stub by default, mock when asserting" as a convention. The trait enforces this well; it's the package's best idea.
  • You can accept a small, single-maintainer dev-only dependency — mitigated by it being require-dev, MIT, and trivially removable (tests fail loudly if it breaks, and nothing ships to production).
  • You have a concrete rollback story: swap one trait import and a helper method for the internal equivalent.

Look elsewhere if:

  • Toolchain gate isn't met: PHPUnit < 12 or PHP < 8.4. Don't force an upgrade for this.
  • You're on Pest. Pest's mock()/fake() helpers and its function-first style cover much of the same ground with a different ergonomics story; mixing the two conventions in one repo is a net loss.
  • Your real pain isn't dependencies. If tests are slow, flaky, or low-coverage because of database calls, queues, or HTTP, this trait fixes none of that. Those need RefreshDatabase, fakes, and contract tests.
  • Mockery is already the house standard. Adoption only pays off if the file uses this trait; mixed conventions cost more than the boilerplate saved. Decide at the module or repo level, not per test.
  • Scalars, enums, or domain-meaningful values are the bulk of your constructor args. Those must be passed explicitly and can't be doubled, so the savings shrink quickly — the trait's leverage is proportional to the number of object dependencies.
  • You need guarantees. No stars and no dependents means no community backstop. If your org requires provenance, security review, or a maintenance SLA on dev dependencies, vendor or reimplement instead; you'll get the same benefit with a known owner.
  • You expect it to cover integration or E2E layers. It won't. Keep it firmly in unit tests and set that expectation with stakeholders up front, or you'll be defending a promise the package never made.

Before you commit, run a short technical spike (half a day, not a sprint) to confirm behavior on your actual code: a service with a scalar-without-default, one with an enum dependency, one with two dependencies of the same type (getMockedService(BasicService::class, 'first')), one nullable dependency, and one with #[Required] setters. That list is exactly where the README says friction lives, so it's the right acceptance test — and the answer is more informative than any star count.

How to Pitch It (Stakeholders)

For executives — frame it as a small, reversible productivity line item, not a platform bet. Our unit tests for service classes spend real engineering time on setup that has nothing to do with what's being tested: every time a service's constructor changes, we rewrite the mock plumbing in its test file. This trait replaces that with a single line — $this->createRealMockedServiceInstance(OrderService::class) — which builds the real service with every dependency automatically filled in. Two honest caveats: it requires our PHP 8.4 / PHPUnit 12 toolchain, so it rides along with an upgrade we already need; and it's a tiny open-source utility (MIT, one maintainer, no dependents), used only in tests and never in production code. I'm not going to quote a made-up productivity percentage. I'd rather spend a day piloting it in one module and bring back measured numbers — setup lines removed per test and time-to-green on a real refactor. If it doesn't pay for itself, removing it is a one-line change.

For engineering — the substance is the mocking semantics, not the auto-wiring. Auto-wiring is reflection over the constructor plus #[Required] setters; anyone can write it. What's worth adopting is the ownership model: dependencies are created as doubles PHPUnit doesn't know about, so unfetched ones never trigger "no expectations configured" noise. You opt a dependency in with getMockedService() (registered → expects() is verified) or getStubbedService() (plain stub, for return values only). That means strict-mode output stays meaningful, and tests assert on the couple of collaborators that matter instead of stubbing everything reflexively. The service is constructed immediately, so you can declare expectations before or after you exercise it — no ordering traps. Duplicate dependency types get distinct doubles and need a parameter name (getMockedService(BasicService::class, 'first')); scalars without defaults and enums must be passed explicitly; nullable deps still get a double, so pass null if that's the intent. createRealPartialMockedServiceInstance covers partial mocks, though those follow normal PHPUnit expectation rules. Two constraints to design around: #[Required] is Symfony's attribute, so the setter-injection half of the feature is only worth much if our services actually use it — Laravel's constructor injection is where the value concentrates. And this is a unit-test-only tool; it does nothing for HTTP, queue, or database-backed feature tests, which should keep using Http::fake(), Queue::fake(), and RefreshDatabase. Proposed next step: one module pilot with the edge-case list above as acceptance criteria, then a go/no-go based on measured maintenance impact — not on the package's star count, which is zero, and not on a vendor's benchmark, which doesn't exist.

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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin