noeldemartin/laravel-dusk-mocking
Laravel Dusk Mocking lets you mock classes and services inside Laravel Dusk browser tests, where ordinary mocks can't reach the separate browser process. Register mocks in your test and have them applied in the application, making end-to-end tests faster and more predictable.
v6.5.3 is a patch release — additive only, no breaking changes, no migrations, no config republish. Bump the constraint (composer update vendor/package) and you're done; the only new surface area is the testing fake.
Where to look first: the new fake() entry point on the package's facade (it wraps Laravel's Illuminate\Support\Facades\Http::fake() under the hood), then the package's own tests/ directory for canonical usage. Because it delegates to the framework, everything you already know about Http::fake() / Http::assertSent() applies — the package fake just pre-wires the base URL, auth headers, and (usually) sensible default stubs so you don't have to hand-roll them.
First use case: a feature test for a controller that calls the third-party API. Before this release you either hit the network, mocked the package's client class, or copy-pasted Http::fake() arrays with the right base URL.
use Illuminate\Support\Facades\Http;
use Vendor\Package\Facades\Package;
it('creates a record on the remote service', function () {
Package::fake([
'api.example.com/v1/records' => Http::response(['id' => 'rec_123'], 201),
]);
$this->post('/records', ['name' => 'Acme'])->assertRedirect('/records');
Http::assertSent(fn ($request) =>
$request->url() === 'https://api.example.com/v1/records'
&& $request['name'] === 'Acme'
);
});
If the package ships default stubs, a bare Package::fake() is often enough to get a suite green — start there, then override only the endpoints whose payloads your assertions care about.
Fake once per test class, override per test. Put the fake in setUp() (or Pest's beforeEach) so every test in the file is hermetic, then pass a URL map in individual tests for the cases that need a specific status or body. The last call wins, so a per-test Package::fake([...]) cleanly supersedes the class-level one.
Cover the base URL, not the whole URL. Matching is Str::is() against the full URL including the query string. Use 'api.example.com/v1/*' rather than pinning exact query strings that will drift.
Sequenced responses for retry/backoff flows. If the package retries on 5xx, Http::fakeSequence() exercises the recovery path deterministically:
Http::fakeSequence()
->push(['error' => 'rate limited'], 429)
->push(['id' => 'rec_123'], 201);
Package::client()->create(['name' => 'Acme']);
Http::assertSentCount(2);
Assert at the HTTP boundary, not on the package. Http::assertSent, assertNotSent, assertNothingSent, and Http::recorded() give you the outgoing Illuminate\Http\Client\Request — URL, method, headers, and decoded body. That's more durable than asserting against internal package classes, which move between minors.
Test jobs and commands the same way. Queue::fake() and Package::fake() compose fine — just remember to Queue::assertPushed() and run the job (or use Bus::fake() + Bus::assertDispatched) if you want the outgoing HTTP assertion to fire.
Queue::fake();
Package::fake();
$this->post('/records', ['name' => 'Acme']);
Queue::assertPushed(SyncRecord::class);
Fail loudly in CI. Add Http::preventStrayRequests() to your base TestCase::setUp(). Any package call that isn't covered by a fake throws instead of silently hitting a sandbox — this is what turns "the fake is wired up" from a convention into a guarantee.
Keep a live path for local/staging. Fakes belong in tests only. For manual verification, point the package at a sandbox base URL via env/config rather than faking inside app code.
Package::fake() does not bleed across test methods — unless you override setUp()/tearDown() without calling parent::, or set the fake in a bootstrap/beforeAll hook (PHPUnit setUpBeforeClass, Pest beforeAll). If you see "unexpected request" failures in a later test, that's the cause.preventStrayRequests(). Call Package::fake() / Http::fake() after (or in the same setup step as) preventStrayRequests(). A stray-prevention call placed after the fake can mask which requests were actually unmatched.Illuminate\Http\Client\ConnectionException (or Http::failedConnection() on newer Laravel) — don't assume the fake exercises your retry/timeout branches for free.Request, not your DTO. JSON bodies are decoded, so $request['name'] works — but nested payloads may differ from what the package sends after transformation. dd(Http::recorded()) is the fastest way to see the real shape.assertSentCount(1) will look wrong. Flush the relevant cache key in setUp() or disable caching in the test config.beforeEach state. A fake defined in beforeEach is re-applied per dataset case, which is what you want — but closures capturing $this can pin stale per-case values. Prefer explicit Package::fake([...]) inside the test body for parameterised cases.--parallel) is safe — each process has its own facade instance and its own fake state. No extra guarding needed.assertSent fails, Laravel dumps the recorded requests; if the output is truncated, Http::recorded() returns the full [Request, Response] pairs. Reach for that before reaching for ray()/dump().Http::fake([...]) alongside Package::fake() when the package calls a second host you also need to stub. Mixing the two is fine — the framework-level fake and the package fake target the same underlying factory.^6.5 is safe for this release, but the fake's default stubs are now part of your test contract; a future minor could change them. Review the 6.x changelog before bumping, and prefer explicit stubs over relying on defaults for anything you assert on.How can I help you explore Laravel packages today?