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

Testing Tools Laravel Package

illuminated/testing-tools

A collection of practical testing utilities for Laravel applications, offering helpful assertions and shortcuts that make feature and unit tests cleaner, faster to write, and easier to maintain. Ideal for teams wanting more expressive test coverage with less boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Install it as a dev-only dependency — it has no runtime role in a shipped application:

composer require --dev dmitry-ivanov/illuminated-testing-tools:^13.0

Sanity-check the package name against the changelog repo (dmitry-ivanov/laravel-testing-tools) on first install — a wrong name fails loudly, but it's a wasted five minutes.

Versioning rule, as of 13.0.0: the package major tracks the Laravel major. 13.x = Laravel 13, 12.x = Laravel 12. Pick the tag that matches your framework version, not the newest tag. The 13.0.0 release is only "Laravel 13 support" — no new assertion helpers, no API changes — so if you're still on Laravel 12, stay on ^12.0 and skip this release entirely.

Look here first, in order:

  1. The README's assertion index — the package is small, and the README is effectively the API surface. Skim the whole list once before writing tests, otherwise you'll re-implement helpers that already exist.
  2. src/ — the package is organized as traits grouped by domain (HTTP/response assertions, database/state assertions, console/Artisan output, mail/notification, view data). Match the trait to the test you're writing.
  3. composer.json — check the illuminate/* and phpunit/phpunit constraints before you commit. With major-aligned versioning the mapping is now trivial: your Laravel major determines your package major, so the constraint check is mostly about PHPUnit compatibility, not about guessing what it was tested against.

First use case: take a test that currently does manual response decoding —

$response = $this->getJson('/api/users/1');
$this->assertSame(200, $response->status());
$body = $response->json();
$this->assertArrayHasKey('data', $body);
$this->assertSame('Jane', $body['data']['name']);
$response->assertHeader('Content-Type', 'application/json');

— and collapse it into the package's response assertion. That single rewrite tells you whether the helper style fits your suite. If the diff isn't clearly shorter and the failure message is still readable, don't adopt further.

Implementation Patterns

Global vs. scoped trait placement. Two viable setups:

  • Pull the traits into tests/TestCase.php so every test class inherits them. Best for a single-team codebase where the whole suite is written in one idiom.
  • Pull them per-test-class. Safer in a large or multi-team suite, and it makes the dependency visible at the call site — when you eventually drop the package, you can grep for the trait use lines.

Layer, don't replace. Laravel's built-in assertJson*, assertDatabaseHas, Mail::fake(), and assertSee are first-class and documented. Use the package where it compresses a multi-line check into one readable line; leave framework assertions alone where they're already one line. A suite that mixes both is fine — a suite where nobody can tell which library an assertion came from is not.

Pest. Traits work under Pest via uses(SomeTrait::class)->in('Feature') in Pest.php, applied at the directory level. If a helper returns a value rather than asserting, wrap it in expect()->extend() so failures render in Pest's diff format instead of a bare boolean.

Compose into your own assertion layer. The durable pattern is a thin project-level trait on top:

trait AssertsApiContract
{
    use \Illuminated\TestingTools\...\ResponseAssertions;

    public function assertUserPayload(array $user, TestResponse $response): void
    {
        $this->assertJsonStructure([...], $response); // package helper
        $response->assertJsonPath('data.id', $user['id']); // framework
    }
}

This keeps package calls in one file. If the package goes unmaintained, the replacement is a single-file edit rather than a sweep of 200 tests.

Upgrade in lockstep with Laravel. Because the majors are aligned, the package bump belongs in the same PR as the Laravel major bump — not a separate dependabot-style chore. Do both, then run the full suite once: that's your only real signal for framework-assertion collisions (see below). A package release that says only "added framework support" is telling you the risk is entirely in the framework's new assertion methods, not in the package.

Data providers / table-driven checks. Packages like this shine when you loop over cases: one assertion per row, each with a distinct message. Pass the case label through so a failure names the input rather than showing three identical assertion names.

CI. Nothing special. Just ensure composer install --no-dev in your production image — the --dev flag is the only thing keeping this out of the artifact.

Gotchas and Tips

Method-name collisions are the real risk, and 13.0.0 is exactly when to look for them. Laravel adds framework assertions every release (assertJsonPath, assertDatabaseCount, assertInvalid, assertSessionHasErrors variants, and whatever Laravel 13 introduced). A trait method with the same name silently overrides the framework method, or fatals if the signature differs. A "support only" package release contains no defensive changes for this — the package author can't know about collisions until users hit them. When you bump to 13.x, run the suite and check for Cannot redeclare or a suddenly-different failure format. If you see one, alias the package method or drop the trait.

Traits assume Illuminate\Foundation\Testing\TestCase. They rely on $this->app, $this->get(), $this->postJson(), and the assertion plumbing. Using them in a plain PHPUnit class — or a Laravel UnitTestCase without CreatesApplication — fails with confusing "call to undefined method" or null-app errors that look like package bugs. Keep them in Feature tests.

Database assertions need the transaction/fresh-database story settled first. If RefreshDatabase isn't applied, database helpers assert against whatever the dev machine's DB happens to hold. Verify with a deliberately-wrong assertion that should fail — if it passes, your isolation is broken, not the helper.

Failure-message quality is the adoption metric. Run one deliberately broken version of each helper you plan to use and read the output. Community helpers sometimes funnel through assertTrue($condition), producing Failed asserting that false is true with no diff. If you hit one of those, wrap it with your own message argument rather than accepting the noise.

Debugging: phpunit --filter=ThatOneTest --debug. If an assertion passes when it clearly shouldn't, check whether the method is actually the package's — dump(method_exists($this, 'assertFoo')) or new ReflectionMethod($this, 'assertFoo') to see which class it resolves to. Trait shadowing is invisible in stack traces.

Version pinning. With zero dependents, you are the canary. Pin the exact version in composer.lock, and read the diff before bumping. The good news is the upgrade path is now deterministic: package major == Laravel major, so there's no "which tag supports my framework" guessing. The bad news is that a major bump with no code changes gives you no diff to read — the release notes tell you everything, and everything is "framework support." Treat that as a green light only if your Laravel major bump is green.

Extension points to prefer: your own wrapper trait (above), or a Response::macro() / TestResponse macro registered in a service provider, rather than editing the package. Never vendor-patch a dev dependency — it won't survive composer update and won't be visible to reviewers.

Exit strategy. These helpers are almost always thin wrappers: assertStatus(200), assertJsonStructure(...), assertDatabaseHas(...). Before adopting broadly, confirm you could inline the body of each helper in under a minute. If a helper embodies real logic (schema diffing, console output parsing), note it — that's the piece you'd have to reimplement if the package stalls. The 13.0.0 release carrying no new helpers is mildly reassuring here: the surface has been stable across a major, so whatever you inline today is unlikely to change shape tomorrow.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource