In a Laravel test, what does Event::fake() stop from running, and how do you fake only some events?
answer
- listeners stop, dispatch is recorded
- model events faked too
- factories needing creating hooks break
- Event::fake([RefundIssued::class])
- except(), fakeExcept(), fakeFor()
basics
~10 sEvent::fake() swaps the dispatcher for a recorder, so no listener runs, including Eloquent model-event listeners and observers. Pass a list, Event::fake([RefundIssued::class]), to fake only those; everything else dispatches normally.
solid answer
~40 s`Event::fake()` replaces the event dispatcher with an `EventFake` and also installs it as Eloquent's model event dispatcher, so **no listener runs**: your own listeners, queued listeners, and the `creating`/`saved` hooks that observers or model `booted()` closures rely on. It records each dispatch for `Event::assertDispatched()`, `assertDispatchedTimes()`, `assertNotDispatched()` and `assertNothingDispatched()`. That breaks factories that fill a UUID or slug in a `creating` hook, so either create models before faking or fake narrowly: `Event::fake([RefundIssued::class])` fakes only that event, `Event::fake()->except([...])` or `Event::fakeExcept([...])` fakes all but some, and `Event::fakeFor(fn () => ...)` fakes only inside a closure and restores the real dispatcher afterwards. `Event::assertListening(RefundIssued::class, SendRefundReceipt::class)` checks the wiring without running the listener.
go deeper
Recall that Event::fake() records events instead of running listeners, and that assertDispatched checks one was fired.
Explain that the fake also replaces Eloquent's model event dispatcher, and pick between a full fake, a listed subset, except() and fakeFor().
Diagnose factory failures caused by a blanket fake in setUp, and split coverage: dispatch asserted in the feature test, wiring by assertListening, the listener tested directly.
Set a team default of narrow event fakes so tests keep model hooks real and only silence the side effects each test is about.
## What `Event::fake()` replaces In Laravel an **event** is an object passed to the dispatcher (`event(new RefundIssued($refund))` or `RefundIssued::dispatch($refund)`), and **listeners** are the classes the dispatcher calls in response. `Event::fake()` swaps the dispatcher behind the `Event` facade for an `EventFake`. From then on a dispatch is **recorded** instead of delivered: no listener's `handle()` runs, and queued listeners are not pushed either. The detail that surprises people is in the facade's `fake()` method itself: besides swapping the facade, it calls `Model::setEventDispatcher($fake)`. Eloquent's model events (`creating`, `created`, `saving`, `deleted` and the rest) go through that dispatcher, so **they are faked too**. Observers and closures registered in a model's `booted()` method stop firing. ## The factory trap Many apps fill columns in model hooks, for example a `creating` hook that sets a UUID or a public reference number. Under a blanket `Event::fake()` that hook never runs, so `Refund::factory()->create()` fails on a NOT NULL column, or saves a row missing the value your test later reads. The docs' advice is direct: call `Event::fake()` **after** creating the models that need their hooks, or fake only the events under test. ## Faking a subset | Call | Effect | |---|---| | `Event::fake()` | every event recorded, no listener runs | | `Event::fake([RefundIssued::class])` | only the listed events recorded; all others dispatch normally, model events included | | `Event::fake()->except([RefundIssued::class])` | everything faked except the listed events | | `Event::fakeExcept([RefundIssued::class])` | shorthand for the line above | | `Event::fakeFor(fn () => ...)` | fake inside the closure only; the real dispatcher is restored in a `finally` block and the closure's return value is returned | | `Event::fakeExceptFor(fn () => ..., [...])` | the `except` variant of `fakeFor` | The list may also hold closures that receive the event name and payload and return whether to fake it. `fakeFor` is the tool when a test must build data with real model events and then observe a single action with events faked: ```php $refund = Event::fakeFor(function () use ($order) { $refund = $order->refunds()->create(['amount' => 1500]); Event::assertDispatched(RefundIssued::class); return $refund; }); // Real dispatcher again: model events and listeners run from here on. ``` ## The assertions - `Event::assertDispatched(RefundIssued::class, fn ($e) => $e->refund->is($refund))` checks that at least one matching dispatch happened. - `Event::assertDispatchedOnce()` and `assertDispatchedTimes($class, 2)` check the count. - `Event::assertNotDispatched()` and `assertNothingDispatched()` check the negative. - `Event::assertListening(RefundIssued::class, SendRefundReceipt::class)` checks that the listener is **registered** for the event. Since the fake stops listeners from running, this is how a test covers the wiring; the listener's own behaviour gets its own test. ## Queued listeners: faking the event or the queue Sometimes the question is not "was the event dispatched" but "did the event reach its queued listener". Under `Event::fake()` a queued listener is never pushed, because the dispatcher never calls it. The alternative is to leave events real and fake the queue instead: the listener is then wrapped in an `Illuminate\Events\CallQueuedListener` job and recorded by `Queue::fake()`, so `Queue::assertPushed(CallQueuedListener::class, fn ($job) => $job->class === SendRefundReceipt::class)` proves the listener was queued. Pick one layer per test: - fake **events** to test the code that fires them; - fake the **queue** to test that the real dispatcher routes an event to a queued listener; - call the listener directly to test what it does. ## Two finer points 1. **After-commit events.** An event implementing `ShouldDispatchAfterCommit` that is dispatched inside a database transaction is recorded only when the transaction commits, mirroring how the real dispatcher delays it. 2. **Fakes are per test.** The application, and with it the fake, is rebuilt for each test, so there is nothing to undo at the end of a test; `fakeFor` exists for switching back *within* one test. ## What to take into an interview - `Event::fake()` stops listeners and model hooks and records dispatches. - Narrow it with a list, `except()`/`fakeExcept()`, or `fakeFor()`. - Create hook-dependent models before a blanket fake. - Assert the event with a closure, and the wiring with `assertListening`.
- A test calls Event::fake() in setUp and then Refund::factory()->create() fails with a NOT NULL error on the uuid column. Why?The model's `creating` hook fills the UUID, and `Event::fake()` also replaces Eloquent's model event dispatcher, so the hook never runs. Create the refund before faking, or fake only the events the test asserts, for example `Event::fake([RefundIssued::class])`, which leaves model events real.
- How do you test that SendRefundReceipt is wired to RefundIssued without running it?Call `Event::fake()` and `Event::assertListening(RefundIssued::class, SendRefundReceipt::class)`. It inspects the listeners registered on the underlying dispatcher. Test the listener's `handle()` separately by constructing it and passing it an event.
Event::fake() is like diverting a company's internal mail to a single intake tray: every memo is logged, nobody downstream acts on it, and that includes the facilities team who normally stamp new files with a number, which is why your freshly created records arrive unstamped.
saying these in an interview costs you the question
- Event::fake() only affects events you dispatch yourself, not Eloquent model events
- A partial Event::fake([X::class]) still stops every listener
- Event::fakeFor() keeps the fake active for the rest of the test
- Faked events still run their listeners so side effects can be asserted
- assertListening() runs the listener to prove it works