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

Peststan Laravel Package

mrpunyapal/peststan

PHPStan extension for Pest PHP that adds type-safe expectations, proper $this binding in test closures, generic expect() inference, type-narrowing assertions, and accurate return types for all Pest functions. Requires PHP 8.2+, PHPStan 2, Pest 3-5.

View on GitHub
Deep Wiki
Context7

Product Decisions This Supports

  • Whether to extend static analysis into the test suite. Today most Laravel/PHP shops run PHPStan on app/ but leave tests/ unanalyzed. PestStan makes tests/ a viable analysis target by teaching PHPStan the Pest DSL, so the decision "do we gate CI on test-code quality?" becomes a concrete, low-cost yes/no rather than a vague aspiration.
  • Build vs. buy vs. fork. The three capabilities that matter here — generic Expectation<T> inference, $this binding + dynamic property inference from beforeEach(), and the semantic expectation rules (pest.expectation.impossible / pest.expectation.redundant) — are the expensive parts to build. Re-implementing them in-house is a multi-sprint effort; adopting is a dev-dependency. Because the package is MIT and archived, a third option exists: vendor/fork a pinned copy and own it. That is a legitimate PM-level decision, not just an engineering one, and it should be made explicitly rather than by default.
  • Roadmap sequencing for test-suite modernization. The requirements (PHP ^8.2, PHPStan ^2.0, Pest ^3|^4|^5) map directly onto upgrade work you may already have planned. If a Pest 4→5 or PHPStan 1→2 migration is on the roadmap, this package belongs in the same workstream — its compatibility matrix becomes a checklist item, and it de-risks the migration by catching Pest API misuse that only surfaces at runtime today.
  • Reducing test-authoring friction (DevEx investment case). The dynamic-property inference is the sleeper feature: beforeEach(fn () => $this->post = new Post) typed without @var annotations or extra locals eliminates the "Cannot access property on mixed" noise that normally pushes teams toward bloated baselines or annotation litter. That's a measurable DevEx win (annotations removed, baseline lines removed) you can put a number against.
  • Test-quality standards and onboarding. The rule set targets real, common mistakes: empty closures, static test closures, beforeAll() inside describe(), duplicate descriptions, repeat(0), non-Throwable classes in throws(), $this in beforeAll(), empty describe() blocks. These are excellent codified standards for a team style guide and for reviewing junior contributors' PRs — the reviewer no longer has to be the linter.
  • Whether to invest in architecture testing. Explicit support for toExtend, classes()->toBeFinal(), toBeInvokable, toBeReadonly, toUseStrictTypes means architecture tests get the same type safety as unit tests. If "enforce hexagonal boundaries / final classes" is a stated engineering goal, this is a supporting capability rather than an incidental one.
  • Adoption governance. Every rule ships a stable PHPStan identifier, so you can decide per rule what's blocking vs. advisory — e.g., hard-fail on pest.expectation.impossible, baseline pest.expectation.redundant for a quarter. That turns a binary "adopt the tool" decision into a phased rollout with a dial.

When to Consider This Package

Consider adopting when:

  • You are on Pest (not PHPUnit-only) and already run PHPStan 2.x in CI; the marginal cost is one dev dependency plus a config include.
  • You are on PHP 8.2+ and Pest 3, 4, or 5 — the supported matrix is current, not legacy.
  • tests/ is inside PHPStan's paths, or you're willing to add it (and can tolerate an initial error burst).
  • You want the diagnostic value more than the tooling novelty: catching an assertion that can never pass (expect(42)->toBeString()) is a genuine bug class that Pest silently accepts at runtime.
  • You have a named owner willing to fork/baseline if the upstream stalls — this is the key gating criterion given the archived status.

Look elsewhere (or defer) when:

  • You're not on Pest. The entire value is Pest-specific; PHPUnit users get nothing.
  • You're on PHPStan 1.x, PHP < 8.2, or Pest 2. Compatibility is explicitly scoped; don't try to bend it.
  • You require vendor support or a maintenance SLA. The repo is archived, has 0 dependents, and 53 stars. Treat it as feature-complete, single-author code: no security response, no bug-fix commitment, no roadmap influence. If your org's policy forbids unmaintained dependencies (even dev-only), stop here — or adopt only under an explicit internal-fork exception.
  • Your team lacks capacity to own it. With zero dependents there is no community backstop; if it breaks against a future Pest release, you fix it or you drop it. Budget a small maintenance reserve before adoption, not after.
  • The rule noise would exceed the rule value. pest.expectation.redundant can flag intentional assertions in tests that exist to document intent. If the team can't absorb a baseline, phase it or ignore that identifier.
  • You need rules outside the shipped set (e.g., assertion-count heuristics, custom Pest plugin typing). The package is not a framework for arbitrary test rules; extending it means writing PHPStan rules yourself, at which point evaluate whether you need the package at all.

How to Pitch It (Stakeholders)

To executives: A small, free, MIT-licensed developer tool that makes our test suite check itself. It adds no runtime dependency, ships nothing to production, and installs as a dev-only Composer package. The payoff is earlier detection of broken tests — tests that can never pass, duplicate test names, hooks placed where Pest forbids them — which today are only found when CI runs slowly or a release goes sideways. Cost is roughly an afternoon of integration plus a one-time cleanup of new findings; the only real caveat is that the upstream project is archived, so we should plan to pin the version and keep a fork option in our back pocket.

To engineering: PestStan is a PHPStan extension that types the Pest DSL properly. Three things you get immediately: expect() carries its generic type through the whole chain (Expectation<string>, Expectation<User>, narrowed by toBeString() / toBeInstanceOf()), $this is correctly typed inside it()/test()/describe() and lifecycle hooks by auto-reading your Pest.php uses() config, and properties assigned in beforeEach() are inferred (unioned across hooks) so you stop writing @var annotations and stop getting "property on mixed" errors. On top of that it ships ~15 analysis rules with stable identifiers, so you can turn them on one at a time and baseline what you don't want yet. Config is near-zero: install, let extension-installer register it, point PHPStan at tests/. Two things to know: it's read-only analysis (no runtime behavior, nothing to deploy), and upstream is archived — so we pin the version, and if a future Pest release breaks it, we either patch our fork or remove it. Recommendation: pilot on one service, measure the new-error yield in the first run, and decide fork-vs-drop within one quarter.

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.
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk