phpunit/phpunit-skeleton-generator
PHPUnit Skeleton Generator creates skeleton test classes from existing production code and skeleton class stubs from test cases, helping PHP developers bootstrap PHPUnit tests quickly with less boilerplate.
What the package is. phpunit/phpunit-skeleton-generator is a dev-only CLI utility (phpunit-skelgen) that reads a production class via reflection and emits a PHPUnit test class skeleton — @covers annotation, one stub method per public method, bodies marked incomplete. It also works in reverse (production class skeleton from a test). It ships no runtime code, no service provider, and nothing that belongs in require rather than require-dev.
Architecture fit. Conceptually it fits as a scaffolding tool in the developer-workstation layer, not the application architecture. In a Laravel codebase the fit is materially weaker than the description implies: generated classes extend PHPUnit\Framework\TestCase directly, not Tests\TestCase, so every output needs manual rebasing before it can use RefreshDatabase, factories, actingAs, or HTTP assertions. The generator has no concept of controllers, form requests, jobs, events, or container bindings, which is where most Laravel test-writing effort actually lives. Realistic value is confined to plain unit-testable classes: value objects, services, actions, DTOs, small domain helpers. For anything Feature-level it produces a stub that has to be rewritten.
Integration feasibility. This is the blocking issue. The final 2.x line targets PHP ^7.0 and PHPUnit ^6.0, which is long EOL. A modern Laravel 10/11 application on PHPUnit 10/11 cannot co-install it — Composer will refuse to resolve, or will attempt to downgrade PHPUnit, which is unacceptable. Workarounds are narrow:
Technical risk. High, and skewed toward silent failure rather than loud failure:
markTestIncomplete. If these land in CI and nobody finishes them, you ship green builds and an inflated test count with zero assertions. This is the single most likely way the tool causes harm.require-dev still runs arbitrary code on developer machines and CI runners.Key questions to answer before spending any time on it:
artisan make:test, Pest's pest:test) that covers 80% of this with zero dependency cost?Stack fit. Poor-to-marginal for a standard Laravel stack. The relevant comparison set: php artisan make:test --unit, pest:test, IDE generation, and a checked-in stubs/ template directory. Those are zero-dependency, framework-aware, and already produce the correct base class and namespace. The generator's only edge is auto-enumerating public methods and emitting @covers per method — a ~40-line internal script or a code template with a method loop reproduces it, and we control it.
Migration path. Do not add it to composer.json. Ranked options:
RefreshDatabase for Feature, namespace inferred from path) plus a short internal generator command. Cost is days, not weeks; payoff is the same and the artifact is maintainable.composer global require or a tools/skelgen/composer.json sandbox purely for one-off unit skeletons on plain classes, invoked manually, never wired into make or CI. Keeps the abandoned constraint out of the app lockfile.Compatibility checklist for whichever route is chosen:
TestCase, @covers semantics changed in PHPUnit 10/11 (metadata via attributes, @covers deprecations).CreatesApplication, RefreshDatabase, WithoutMiddleware).composer.json autoload-dev, or does it need manual fixing every time?Sequencing if we pilot anyway:
make:test + hand-editing. If the fix-up time exceeds the typing it saved, stop.Maintenance. Effectively zero upstream. There is no release cadence to track because there are no releases. Every PHP or PHPUnit upgrade becomes our problem. The true cost is not the install — it is the fork debt if we go route 3 above.
Support. No maintainer triage, no documented support channel, no CVE process. Any breakage is diagnosed by whoever adopted it. For a dev-only tool that is survivable; for anything referenced from CI templates or onboarding docs it becomes a recurring interruption.
Scaling. Invocation cost is trivial (single-process CLI, one class at a time). The scaling problem is organizational: the tool works fine for one engineer on one machine and degrades badly as a standard, because every developer must independently remember the base-class rewrite, the namespace fix, and the fact that generated tests are empty. That knowledge does not survive onboarding.
Failure modes to plan for:
markTestIncomplete in new files.conflict guard in composer.json is cheap insurance regardless of the decision.Ramp-up. Low for an individual (one README read), high for a team (must teach base-class rebasing, namespace correction, and the discipline of finishing stubs). Ramp-up cost exceeds benefit at team scale.
Bottom line: do not adopt. The capability is real but thin, the package is abandoned and dependency-incompatible with any current Laravel/PHPUnit stack, and the same outcome is available from artisan make:test, IDE generation, or a few days of internal tooling that we would own outright. The zero dependents and zero score are consistent with what the code inspection shows — this is a historical artifact, not a component. Recommended action: decline the dependency, run a two-day spike on a house test-stub generator, and revisit only if that spike fails.
How can I help you explore Laravel packages today?