How do you test a Livewire sign-up component that depends on the logged-in user, a ?ref= query parameter and an uploaded CSV of contacts?
answer
- Livewire::actingAs($user)->test()
- withQueryParams feeds #[Url] properties
- set() with UploadedFile::fake()
- upload flow simulated, temp disk faked
- withCookies and withHeaders for the rest
basics
~10 sChain Livewire::actingAs($member) and Livewire::withQueryParams(['ref' => 'footer']) before test(), then set('contacts', UploadedFile::fake()->create('contacts.csv', 4, 'text/csv')); Livewire simulates the whole temporary-upload flow for you.
solid answer
~40 s`Livewire` offers setup methods that return the manager so they chain into `test()`. `Livewire::actingAs($member)` sets the authenticated user on the guard, optionally a named one, for the rest of the test. `Livewire::withQueryParams(['ref' => 'footer'])` supplies the query string for the initial render, so a `#[Url] public string $ref` property starts as `'footer'`. `withCookies()` and `withHeaders()` do the same for cookies and headers. For uploads, pass an `Illuminate\Http\UploadedFile` to `set()`: `set('contacts', UploadedFile::fake()->create('contacts.csv', 4, 'text/csv'))`. Livewire then runs its start, store and finish upload calls itself, putting the file on a temporary disk it fakes during tests and applying the temporary-upload validation, so the property ends up holding a `TemporaryUploadedFile` exactly as in the browser.
go deeper
Recall Livewire::actingAs($user)->test() for logged-in components and set('file', UploadedFile::fake()->...) for uploads.
Explain withQueryParams for #[Url] properties, how set() simulates the upload steps, and which temporary-upload rules apply.
Build realistic fixtures: content-bearing fake files, users on the right guard, and query-driven state, without leaking auth between tests.
Decide which environmental concerns belong in component tests and which in HTTP or browser tests, and standardise the fixtures teams reuse.
## Setting up the world before `test()` A **Livewire component test** often needs context the browser would provide: who is logged in, what is in the URL, which file was chosen. Livewire's testing entry points set that context before the first render. | Setup | What it does | |---|---| | `Livewire::actingAs($user, $guard = null)` | Sets the user on the guard and makes it the default | | `Livewire::withQueryParams(['ref' => 'footer'])` | Query string for the initial render (alias `withUrlParams`) | | `Livewire::withCookie('name', 'v')` / `withCookies([...])` | Cookies for the initial render | | `Livewire::withHeaders([...])` | Request headers | | `Livewire::withoutLazyLoading()` | Renders lazy child components immediately | Each returns the Livewire manager, so they chain: `Livewire::actingAs($member)->withQueryParams([...])->test(NewsletterSignup::class)`. ## The logged-in user `actingAs()` puts the user on the auth guard for the whole test. Inside the component, `auth()->user()` and `Auth::id()` return that user, and `#[Authorize]` or `$this->authorize()` checks run against them. Because component tests run without middleware, `actingAs()` is how you supply a user at all: the page's `auth` middleware is not there to redirect a guest. ## Query parameters and `#[Url]` A property marked with Livewire's `#[Url]` attribute syncs with the query string: on the initial render it reads its value from the URL. `withQueryParams()` provides that URL: ```php Livewire::withQueryParams(['ref' => 'footer']) ->test(NewsletterSignup::class) ->assertSet('ref', 'footer'); ``` Without it, the property keeps its declared default, which is a common source of "works in the browser, fails in the test" confusion. ## Uploaded files Livewire uploads are a multi-step browser dance: the client asks to start an upload, sends the file to a signed endpoint that validates and stores it in a temporary directory, then tells the component which temporary file to use. In a test, `set()` recognises an `UploadedFile` (or an array of them for multiple uploads) and performs those steps itself: 1. calls the component's internal start-upload action; 2. validates and stores the file on the temporary-upload disk; during unit tests that disk is `tmp-for-tests`, which Livewire fakes automatically; 3. calls the finish action, so the property holds a `Livewire\Features\SupportFileUploads\TemporaryUploadedFile`. If the file fails the temporary-upload rules, which default to `['required', 'file', 'max:12288']` (12 MB) unless `livewire.temporary_file_upload.rules` overrides them, the error lands on the property and `assertHasErrors('contacts')` sees it. Use Laravel's fake factory to build the file: - `UploadedFile::fake()->create('contacts.csv', 4, 'text/csv')`: a named file of 4 KB with a MIME type; - `UploadedFile::fake()->image('avatar.png', 200, 200)`: a real image, for dimension rules. Where the component then **stores** the file, `$this->contacts->store('imports')`, is ordinary Laravel storage; faking that disk with `Storage::fake()` and asserting on it is the storage-fakes toolbox, not a Livewire API. ## Putting it together ```php it('imports contacts for a member', function () { $member = User::factory()->create(); Livewire::actingAs($member) ->withQueryParams(['ref' => 'footer']) ->test(NewsletterSignup::class) ->assertSet('ref', 'footer') ->set('contacts', UploadedFile::fake()->create('contacts.csv', 4, 'text/csv')) ->call('import') ->assertHasNoErrors() ->assertSee('Import queued'); }); ``` ## Several files and other guards For a property that holds many files, pass an **array** of fakes: `set('attachments', [UploadedFile::fake()->create('a.csv', 2), UploadedFile::fake()->create('b.csv', 3)])`. Livewire recognises the array of `UploadedFile` objects and runs the multiple-upload variant of the same steps, leaving an array of temporary files on the property. For apps with more than one guard, `Livewire::actingAs($admin, 'admin')` sets the user on that guard and makes it the default for the rest of the test, which matches what `auth()->user()` inside the component will read. ## Pitfalls - **Order matters**: setup calls must come before `test()`; they only affect the initial render. - **`actingAs` persists** for the rest of the test method, so a later `Livewire::test()` in the same test is authenticated too. - **Query params are initial-render only**; later changes go through `set()`. - **Fake files have fake content**: `create()` makes a sized file, not a valid CSV. Use `createWithContent()` when the component parses the file.
- Why does a Livewire test set() with UploadedFile::fake() not need Storage::fake() for the temporary upload?During unit tests Livewire stores temporary uploads on a `tmp-for-tests` disk and fakes that disk itself the first time it is used. You only fake a disk yourself when the component moves the file somewhere permanent with `store()` and you want to assert it landed there.
- A #[Url] property starts empty in a Livewire test although the page URL has ?ref=footer. What is missing?Component tests do not go through the page URL, so there is no query string unless you provide one. Chain `Livewire::withQueryParams(['ref' => 'footer'])` before `test()`; the initial render then reads `ref` from it.
saying these in an interview costs you the question
- UploadedFile::fake() must be assigned to the property directly, bypassing set().
- withQueryParams() can be called after test() to change the URL mid-test.
- Component tests redirect guests because the route's auth middleware runs.
- Livewire tests cannot exercise file uploads without a browser.
- UploadedFile::fake()->create() produces a valid CSV the component can parse.