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://msaied.com) - [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [About](https://msaied.com#about)

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

Menu
----

Close 

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

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

 1. [Home](https://msaied.com)
2. /
3. [Articles](https://msaied.com/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://msaied.com#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://msaied.com/articles?search=filament)
- [pest](https://msaied.com/articles?search=pest)
- [testing](https://msaied.com/articles?search=testing)
- [laravel](https://msaied.com/articles?search=laravel)
- [livewire](https://msaied.com/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://msaied.com#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://msaied.com#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://msaied.com/articles/laravel-legacy-bridge-carry-authenticated-sessions-from-a-legacy-php-app-into-laravel) [Next articleNew in Laravel 13: First-Party Image Manipulation](https://msaied.com/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://msaied.com#contact) 

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

 [ ![](https://cdn.msaied.com/776/8e8ee70ebeedf358233b53e19ecae4a1.png) Laravel · 4 min read### Filament v5.10.2 Released: Accessibility Fixes, RTL Support, and New Features

13 Oct 2026 ](https://msaied.com/articles/filament-v5102-released-accessibility-fixes-rtl-support-and-new-features) [ ![](https://cdn.msaied.com/772/751d21c9bbf4187742acfb130ad4b3c4.png) Laravel · 3 min read### Puzzle Party: A Laravel + Flutter App Built With the Help of the Community

12 Oct 2026 ](https://msaied.com/articles/puzzle-party-a-laravel-flutter-app-built-with-the-help-of-the-community) [ ![](https://cdn.msaied.com/775/9ffc1bfe88806626f495f3ef307e0b69.png) Composer Pacakge · 4 min read### BashBox: A Sandboxed Bash Interpreter for AI Agents in PHP

11 Oct 2026 ](https://msaied.com/articles/bashbox-a-sandboxed-bash-interpreter-for-ai-agents-in-php-3) 

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

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

 [Discuss your project ↗](https://msaied.com#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://msaied.com)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/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://msaied.com/sitemap.xml)
