Testing Filament v4 with Pest: Forms &amp; Actions | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Filament v4 Testing with Pest: Resources, Actions, and Form Assertions

 Filament v4 Testing with Pest: Resources, Actions, and Form Assertions
=======================================================================

 A practical guide to testing Filament v4 panels using Pest — covering resource page assertions, table action dispatch, and form field validation without spinning up a browser.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 15 Jul 2026 · Updated 15 Jul 2026 · 3 min read

ShareCopy linkCopied

 ![Filament v4 Testing with Pest: Resources, Actions, and Form Assertions](https://cdn.msaied.com/426/a81430a378b2849b6fbecb8235b9119f.png) 

  On this page +1. [Why Testing Filament Panels Is Different](#why-testing-filament-panels-is-different)
2. [Setting Up the Test Environment](#setting-up-the-test-environment)
3. [Testing a List Resource Page](#testing-a-list-resource-page)
4. [Testing Create and Edit Forms](#testing-create-and-edit-forms)
5. [Nested Repeater Fields](#nested-repeater-fields)
6. [Testing Custom Table Actions](#testing-custom-table-actions)
7. [Testing Infolists](#testing-infolists)
8. [Key Takeaways](#key-takeaways)

 Why Testing Filament Panels Is Different
----------------------------------------

Filament v4 renders UI through Livewire components. That means your test suite can drive the full resource lifecycle — list, create, edit, delete — without a browser, using `Livewire::test()` and Filament's own test helpers. The trick is knowing *which layer* to assert against.

---

Setting Up the Test Environment
-------------------------------

Install the Filament testing utilities alongside Pest:

```bash
composer require --dev pestphp/pest pestphp/pest-plugin-livewire

```

In `TestCase.php`, authenticate as a panel user before each test:

```php
use App\Models\User;
use Filament\Facades\Filament;

beforeEach(function () {
    $user = User::factory()->create();
    $this->actingAs($user);
    Filament::setCurrentPanel(Filament::getPanel('admin'));
});

```

Setting the current panel is essential — without it, policy checks and navigation guards resolve against the wrong context.

---

Testing a List Resource Page
----------------------------

```php
use App\Filament\Resources\OrderResource\Pages\ListOrders;
use Filament\Tables\Actions\DeleteAction;

it('renders the orders list', function () {
    livewire(ListOrders::class)
        ->assertSuccessful()
        ->assertCountTableRecords(5);
});

it('can delete an order from the table', function () {
    $order = Order::factory()->create();

    livewire(ListOrders::class)
        ->callTableAction(DeleteAction::class, $order)
        ->assertHasNoTableActionErrors();

    expect(Order::find($order->id))->toBeNull();
});

```

`assertCountTableRecords` counts rows *after* applying the default query, so it respects global scopes and any `modifyQueryUsing` you have on the table.

---

Testing Create and Edit Forms
-----------------------------

Filament v4 uses the unified Schema API. Form fields are still addressable by their `name` attribute:

```php
use App\Filament\Resources\OrderResource\Pages\CreateOrder;

it('validates required fields on create', function () {
    livewire(CreateOrder::class)
        ->fillForm([
            'customer_id' => null,
            'total' => -10,
        ])
        ->call('create')
        ->assertHasFormErrors([
            'customer_id' => 'required',
            'total' => 'min',
        ]);
});

it('creates an order with valid data', function () {
    $customer = Customer::factory()->create();

    livewire(CreateOrder::class)
        ->fillForm([
            'customer_id' => $customer->id,
            'total' => 150.00,
            'status' => 'pending',
        ])
        ->call('create')
        ->assertHasNoFormErrors();

    expect(Order::where('customer_id', $customer->id)->exists())->toBeTrue();
});

```

### Nested Repeater Fields

Repeater state is passed as an indexed array:

```php
->fillForm([
    'line_items' => [
        ['product_id' => 1, 'qty' => 2],
        ['product_id' => 3, 'qty' => 1],
    ],
])

```

---

Testing Custom Table Actions
----------------------------

Custom actions registered with `Action::make('approve')` are callable by name:

```php
it('approves an order', function () {
    $order = Order::factory()->pending()->create();

    livewire(ListOrders::class)
        ->callTableAction('approve', $order, data: [
            'note' => 'Looks good',
        ])
        ->assertNotified();

    expect($order->fresh()->status)->toBe('approved');
});

```

`assertNotified()` checks that a Filament notification was dispatched — a clean proxy for "the action completed without throwing".

---

Testing Infolists
-----------------

Filament v4 infolists are also Livewire-driven. Assert entry state on the view page:

```php
use App\Filament\Resources\OrderResource\Pages\ViewOrder;

it('displays the order total in the infolist', function () {
    $order = Order::factory()->create(['total' => 299.99]);

    livewire(ViewOrder::class, ['record' => $order->getRouteKey()])
        ->assertSuccessful()
        ->assertInfolists()
        ->assertInfolists(fn ($infolist) =>
            $infolist->has('total')
        );
});

```

> **Note:** `assertInfolists` with a closure is available in Filament v4's test helpers. For entry *values*, read the component state directly via `->get('infolist.total')` if the helper isn't available yet in your build.

---

Key Takeaways
-------------

- Always call `Filament::setCurrentPanel()` in `beforeEach` — panel context affects auth, navigation, and policies.
- Use `assertHasFormErrors` with field-rule pairs for precise validation assertions.
- `callTableAction` accepts a `data` array for modal-based actions.
- `assertCountTableRecords` respects your resource's base query, including global scopes.
- Test infolist entries via the view page Livewire component, not a separate render path.
- Keep one assertion per test; Filament's Livewire layer is fast enough that granular tests don't slow CI meaningfully.

- [filament](https://www.msaied.com/public/articles?search=filament)
- [pest](https://www.msaied.com/public/articles?search=pest)
- [testing](https://www.msaied.com/public/articles?search=testing)
- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [livewire](https://www.msaied.com/public/articles?search=livewire)

 Frequently asked questions 
---------------------------

  Do I need a real database to test Filament resources with Pest?Yes. Filament's test helpers drive actual Livewire components that query Eloquent models, so you need a database. Use RefreshDatabase or a SQLite in-memory connection in phpunit.xml for fast, isolated runs.

   How do I test a Filament action that opens a modal with a form?Use `callTableAction('actionName', $record, data: \[...\])` and pass the modal form values in the `data` array. Follow up with `assertHasNoTableActionErrors()` to confirm the form passed validation.

   Can I test Filament pages that require specific permissions?Yes. Create a user with the required roles or permissions before the test, authenticate with `actingAs`, and set the panel context. Filament resolves policies against the authenticated user, so your permission checks run as normal.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleLaravel Legacy Bridge: Carry Authenticated Sessions from a Legacy PHP App into Laravel](https://www.msaied.com/public/articles/laravel-legacy-bridge-carry-authenticated-sessions-from-a-legacy-php-app-into-laravel) [Next articleNew in Laravel 13: First-Party Image Manipulation](https://www.msaied.com/public/articles/new-in-laravel-13-first-party-image-manipulation)  

   On this page
-------------

1. [Why Testing Filament Panels Is Different](#why-testing-filament-panels-is-different)
2. [Setting Up the Test Environment](#setting-up-the-test-environment)
3. [Testing a List Resource Page](#testing-a-list-resource-page)
4. [Testing Create and Edit Forms](#testing-create-and-edit-forms)
5. [Nested Repeater Fields](#nested-repeater-fields)
6. [Testing Custom Table Actions](#testing-custom-table-actions)
7. [Testing Infolists](#testing-infolists)
8. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/743/8998fac3a41451ab3fe1588194e17a43.png) Filament · 3 min read### Securing Filament Plugins with Plumb: Automated Security Scoring for PHP Packages

5 Oct 2026 ](https://www.msaied.com/public/articles/securing-filament-plugins-with-plumb-automated-security-scoring-for-php-packages) [ ![](https://cdn.msaied.com/742/2d02018669cdeedccb5de2efb898f0ee.png) Filament · 3 min read### Filament v3.3.56 Released: File Hash Names and Livewire Upload Fix

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v3356-released-file-hash-names-and-livewire-upload-fix) [ ![](https://cdn.msaied.com/741/5b55c123ad08e4d34e1f4b99ad6a428b.png)  · 3 min read### Filament v4 Schema-Based Forms, Infolists, and the Unified Schema API

5 Oct 2026 ](https://www.msaied.com/public/articles/filament-v4-schema-based-forms-infolists-and-the-unified-schema-api-5) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
