ace-of-aces/intellipest
Intellipest is a Pest extension for Laravel/PHP that adds intelligent test automation and helpers, making it easier to generate, organize, and run tests with less boilerplate so teams can ship reliable code faster.
IntelliPest exists to solve one specific annoyance: Pest's API is built almost entirely on magic methods (__call, HigherOrderExpectation, dynamic $this->assert* calls on the TestCase), so PHPStan, Psalm, and plain IDEs see a wall of "undefined method" errors in tests/. IntelliPest ships the stubs/extensions that teach your tooling about those dynamic calls.
Minimal path:
Install as a dev dependency — this is test-time tooling, never runtime:
composer require --dev ace-of-aces/intellipest
Find the integration point first. Before writing any config, open vendor/ace-of-aces/intellipest/ and look for:
extension.neon → a PHPStan extension (most likely mode for this package). Register it in your analysis config.composer.json extra.laravel.providers → package auto-discovery; you may get a config/intellipest.php to publish.php artisan intellipest:generate) → IDE-stub mode, analogous to laravel-ide-helper.Read the README's install block and follow its exact include path — don't guess.
Wire it into PHPStan (typical case). Create a dedicated phpstan-tests.neon so you don't loosen your app-level rules:
includes:
- vendor/larastan/larastan/extension.neon
- vendor/ace-of-aces/intellipest/extension.neon
parameters:
level: 6
paths:
- tests
Include order matters: Larastan first, then IntelliPest, then your own overrides.
First real use case: run vendor/bin/phpstan analyse -c phpstan-tests.neon on an unchanged test suite. The win is not new errors — it's the disappearance of false positives on expect($user)->name->toBe('Nuno'), expect($x)->toBeTrue(), and $this->get('/')->assertOk(). Once those are quiet, dial the level up and the errors that remain are usually genuine bugs in your tests.
Add a composer script so this becomes muscle memory:
"scripts": {
"test:analyse": "phpstan analyse -c phpstan-tests.neon --memory-limit=1G"
}
Separate CI job for tests. App code and test code want different strictness. Run PHPStan twice in CI — once against app/ at your normal level, once against tests/ with this extension — so test-specific suppressions never leak into production analysis.
Custom expectations need an escape hatch. If you extend Pest's expectation API:
expect()->extend('toBeValidUuid', fn () => ...);
Static analyzers generally can't infer this from a runtime extend() call. Expect to add either a @method docblock on a local Expectation stub or a small project-level stub file. Treat IntelliPest as covering core Pest, and your own extensions as your responsibility.
Pair with Larastan, don't replace it. Larastan types the Laravel TestCase methods ($this->get(), $this->assertDatabaseHas()); IntelliPest types the Pest layer on top (expect(), higher-order tests, test()/it() signatures). You want both includes present — a common misconfiguration is adding one and assuming it covers the other.
Higher-order test typing. Patterns like it('has a name')->expect(fn () => $user->name)->toBe('Nuno') and dataset-driven ->with([...]) are where analyzers get confused. After installing, verify these three shapes specifically with phpstan describe tests/Feature/UserTest.php and inspect inferred return types.
Regenerate IDE stubs in your bootstrap flow. If the package generates helper files, hook regeneration into composer install post-autoload or a make setup step so new contributors don't open the IDE against stale stubs. Gitignore the generated file unless your team prefers committing it (committing reduces IDE "first-open" friction; gitignoring avoids merge conflicts — pick one and document it).
Architecture tests get analysed too. Pest 3 arch() chains are PHP code; they're subject to the same rules. Keep them in tests/Arch and let them run through the same config — they're cheap and catch namespace drift.
Incremental adoption: install → regenerate baseline-free run → fix or baseline only true positives → bump level one notch per sprint. Don't jump to level 8 on a mature test suite in one commit.
pestphp/pest and this package, and upgrade them together in a dedicated PR you can revert.composer install --no-dev breaks CI analysis. PHPStan needs the test dependencies present to resolve Pest symbols. If your analysis stage runs in a production-optimized image, it will fail confusingly.vendor/bin/phpstan clear-result-cache (or --clear-result-cache) — stale caches from the pre-extension run are a classic "why is it still erroring" moment.barryvdh/laravel-ide-helper already types a method, a second declaration can produce duplicate-signature warnings. Check the error type before "fixing" it — sometimes the right move is removing your own stub, not adding one.excludePaths; otherwise you'll be linting machine-generated code.$this inside closures and beforeEach can lose its type. If IntelliPest doesn't resolve it, annotate the closure or extract a typed helper method rather than reaching for @phpstan-ignore.phpstan analyse --debug prints the loaded extensions and their order — the fastest way to confirm IntelliPest is actually registered rather than silently skipped by a malformed includes: path.composer.lock, pin the version, and keep a note on how to drop the include if it becomes a liability — removal is a one-line change, which is exactly why this kind of package is low-risk to trial.parameters: keys the extension exposes (e.g. extra stub paths) — those are where you add project-specific stubs without forking.How can I help you explore Laravel packages today?