Testing Filament v4 with Pest: Forms &amp; Actions | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://msaied.com) [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles  ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://msaied.com) [ Projects ](https://msaied.com/projects) [ Articles ](https://msaied.com/articles) [ Certificates ](https://msaied.com/certificates) [ Contact ](https://msaied.com#contact-section) 

  [ home ](https://msaied.com)    [ articles ](https://msaied.com/articles)    Filament v4 Testing with Pest: Resources, Actions, and Form Assertions        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)

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

  #filament   #pest   #testing   #laravel   #livewire  

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

     15 Jul 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Why Testing Filament Panels Is Different  ](#why-testing-filament-panels-is-different)
2. [  02   Setting Up the Test Environment  ](#setting-up-the-test-environment)
3. [  03   Testing a List Resource Page  ](#testing-a-list-resource-page)
4. [  04   Testing Create and Edit Forms  ](#testing-create-and-edit-forms)
5. [  05   Nested Repeater Fields  ](#nested-repeater-fields)
6. [  06   Testing Custom Table Actions  ](#testing-custom-table-actions)
7. [  07   Testing Infolists  ](#testing-infolists)
8. [  08   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.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ffilament-v4-testing-with-pest-resources-actions-and-form-assertions&text=Filament+v4+Testing+with+Pest%3A+Resources%2C+Actions%2C+and+Form+Assertions) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ffilament-v4-testing-with-pest-resources-actions-and-form-assertions) 

 Frequently Asked Questions 
----------------------------

  3 questions  

     Q01  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. 

      Q02  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. 

      Q03  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. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://msaied.com/articles) 

 [ ![New in Laravel 13: First-Party Image Manipulation](https://cdn.msaied.com/427/504aed7f9f1c4a1cd411c7c4a4b6bc7c.png) Laravel 13 Image Manipulation PHP 

### New in Laravel 13: First-Party Image Manipulation

Laravel 13 introduces a first-party image manipulation feature, removing the need for third-party packages for...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 15 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/new-in-laravel-13-first-party-image-manipulation) [ ![Laravel Legacy Bridge: Carry Authenticated Sessions from a Legacy PHP App into Laravel](https://cdn.msaied.com/429/5abced4ee695d6c03e3ae41bbf2fe4bb.png) Laravel PHP Session Management 

### Laravel Legacy Bridge: Carry Authenticated Sessions from a Legacy PHP App into Laravel

Laravel Legacy Bridge is a package that reads a legacy session cookie, decodes the payload from the legacy dat...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 15 Jul 2026     4 min read  

  Read    

 ](https://msaied.com/articles/laravel-legacy-bridge-carry-authenticated-sessions-from-a-legacy-php-app-into-laravel) [ ![First-Party Image Processing in Laravel 13.20](https://cdn.msaied.com/428/f426d416be5b32c85a6eb69b1dbb5d74.png) Laravel Image Processing Laravel 13 

### First-Party Image Processing in Laravel 13.20

Laravel 13.20 ships a built-in Image facade with an immutable, driver-based API for resizing, cropping, and fo...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 15 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/first-party-image-processing-in-laravel-1320) 

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

Explore

- [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [Contact](https://msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
