---
url: /reference/testing.md
description: >-
  Test your app with Media Library Pro: factories, Storage::fake, Livewire tests
  for the picker and browser, and how to run the package's own suite.
---

# Testing

## Factories

`MediaItem::factory()` and `MediaFolder::factory()` are available once the package is installed.

| Factory | State | What it does |
|---|---|---|
| `MediaItem` | default | A public JPEG record (1600 x 1067) with no file behind it. Enough for lists, pickers and attachments. |
| `MediaItem` | `withImage($width = 640, $height = 480)` | Also writes a real PNG through Spatie, so URLs and conversions work. |
| `MediaItem` | `document()` | A PDF record: `application/pdf`, type `Document`, no dimensions. |
| `MediaItem` | `private($ownerId = null)` | Private visibility. Pass the uploader's id so they can see it. |
| `MediaFolder` | default | A public folder with a random name. |
| `MediaFolder` | `private($ownerId = null)` | A private folder owned by that user. |

```php
use Hoceineel\FilamentMediaLibrary\Models\MediaFolder;
use Hoceineel\FilamentMediaLibrary\Models\MediaItem;

$folder = MediaFolder::factory()->create(['name' => 'Campaign']);
$photo = MediaItem::factory()->withImage()->create(['folder_id' => $folder->id]);
$secret = MediaItem::factory()->private($user->id)->create();
$brochure = MediaItem::factory()->document()->create(['name' => 'Brochure']);
```

## Disks

Fake the library disk so tests never touch real storage. `withImage()` and uploads write to it.

```php
use Illuminate\Support\Facades\Storage;

beforeEach(function () {
    Storage::fake('public');
});
```

Use the disk name from `filament-media-library.disk`. The package's own tests also set `media-library.queue_conversions_by_default` to `false` so conversions run inline.

## Authenticating

Sign in a user who can access your panel, and for a tenant panel, set the tenant:

```php
use Filament\Facades\Filament;

$this->actingAs($user);
Filament::setCurrentPanel(Filament::getPanel('admin'));
Filament::setTenant($team);
Filament::bootCurrentPanel();
```

Call `bootCurrentPanel()` after `setTenant()` so the plugin turns on tenant scoping. The default policies allow any signed-in user, so register your own policy in the test or use `Gate::before` to test denials:

```php
Gate::before(fn ($user, string $ability) => $ability === 'viewAny' ? false : null);

$this->get(MediaLibraryPage::getUrl(['tenant' => $team]))->assertForbidden();
```

## Testing a form with the picker

A form that uses `MediaPicker` is tested like any Filament form. Set the field to an item id:

```php
use Livewire\Livewire;

$item = MediaItem::factory()->create();

Livewire::test(CreatePost::class)
    ->fillForm(['title' => 'Launch', 'cover_id' => $item->id])
    ->call('create')
    ->assertHasNoFormErrors();

expect(Post::query()->sole()->cover_id)->toBe($item->id);
```

For a relationship field, pass an array of ids, and read back with `getMediaLibraryItems('gallery')`. The order you send is the order stored.

## Testing the browser

`MediaBrowser` is a Livewire component:

```php
use Hoceineel\FilamentMediaLibrary\Livewire\MediaBrowser;

MediaItem::factory()->create(['name' => 'Root photo']);

Livewire::test(MediaBrowser::class)->assertSee('Root photo');
```

Test the page itself with a request:

```php
$this->get(MediaLibraryPage::getUrl(['tenant' => $team]))->assertOk();
```

## Outside panels

Turn on the standalone mode for the test:

```php
config()->set('filament-media-library.standalone.enabled', true);

Livewire::test(MediaPickerInput::class, ['name' => 'cover_id', 'image' => true])
    ->set('value', (string) $photo->id);
```

The picker's configuration properties are locked, so a test that tries to `set()` one throws Livewire's `CannotUpdateLockedPropertyException`. That is the behavior you want.

## Running the package's suite

From the package root:

```bash
composer test
composer test-parallel
```

`composer test` runs `vendor/bin/pest`. `composer test-parallel` runs `vendor/bin/pest --parallel`, which is faster; chunk directories and fake storage are scoped per worker. Other scripts: `composer format` (Pint) and `composer analyse` (PHPStan).

The suite uses Orchestra Testbench with SQLite in memory. To run it on MySQL or PostgreSQL, set `DB_CONNECTION` and the usual connection variables before you run Pest:

```bash
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=media_library_test DB_USERNAME=root DB_PASSWORD= vendor/bin/pest
```

```bash
DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=media_library_test DB_USERNAME=postgres DB_PASSWORD=secret vendor/bin/pest
```

`DB_CONNECTION` selects the connection name in `database.default`. Create the empty database first.
