Testing Filament v3 with Pest: Resources &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)    Testing Filament Resources, Actions, and Form Assertions with Pest        On this page       1. [  Why Filament Testing Feels Hard (and How to Fix It) ](#why-filament-testing-feels-hard-and-how-to-fix-it)
2. [  Setting Up the Test Environment ](#setting-up-the-test-environment)
3. [  Testing a List Table ](#testing-a-list-table)
4. [  Testing Table Actions ](#testing-table-actions)
5. [  Testing Create and Edit Forms ](#testing-create-and-edit-forms)
6. [  Testing Page-Level Actions (Header Actions) ](#testing-page-level-actions-header-actions)
7. [  Key Takeaways ](#key-takeaways)

  ![Testing Filament Resources, Actions, and Form Assertions with Pest](https://cdn.msaied.com/550/18c282cafc696a5836ca7e2d380d22ef.png)

  #filament   #pest   #testing   #laravel  

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

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

       Table of contents

1. [  01   Why Filament Testing Feels Hard (and How to Fix It)  ](#why-filament-testing-feels-hard-and-how-to-fix-it)
2. [  02   Setting Up the Test Environment  ](#setting-up-the-test-environment)
3. [  03   Testing a List Table  ](#testing-a-list-table)
4. [  04   Testing Table Actions  ](#testing-table-actions)
5. [  05   Testing Create and Edit Forms  ](#testing-create-and-edit-forms)
6. [  06   Testing Page-Level Actions (Header Actions)  ](#testing-page-level-actions-header-actions)
7. [  07   Key Takeaways  ](#key-takeaways)

 Why Filament Testing Feels Hard (and How to Fix It)
---------------------------------------------------

Filament generates a lot of UI surface area from a small amount of PHP. That density is its superpower in production, but it makes engineers reach for browser tests when unit or feature tests would do. The result is slow, flaky suites.

The good news: Filament ships first-class testing helpers built on top of Livewire's testing API. Pair those with Pest's expressive syntax and you get a fast, readable suite that catches real regressions.

---

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

Install the testing peer dependencies and make sure your `TestCase` boots the Filament panel:

```php
// tests/TestCase.php
use Filament\FilamentServiceProvider;

protected function getPackageProviders($app): array
{
    return [FilamentServiceProvider::class];
}

```

For panel-based apps (the common case), authenticate as a user that passes your panel's `authGuard` before every resource test:

```php
// tests/Pest.php
use App\Models\User;

uses(Tests\TestCase::class)->beforeEach(function () {
    $this->actingAs(User::factory()->create());
})->in('Feature/Filament');

```

---

Testing a List Table
--------------------

Filament's `livewire()` helper mounts the resource's `ListRecords` page. Assert that records appear and that columns render expected values:

```php
use App\Filament\Resources\OrderResource\Pages\ListOrders;
use App\Models\Order;

it('lists orders on the table', function () {
    $orders = Order::factory(3)->create();

    livewire(ListOrders::class)
        ->assertCanSeeTableRecords($orders)
        ->assertCountTableRecords(3);
});

```

Need to verify a specific column value? Use `assertTableColumnStateSet`:

```php
livewire(ListOrders::class)
    ->assertTableColumnStateSet('status', 'pending', record: $orders->first());

```

---

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

Table actions are where most bugs hide. Test the full lifecycle — mounting, filling a form inside the action modal, and asserting the side-effect:

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

it('cancels an order via table action', function () {
    $order = Order::factory()->create(['status' => 'pending']);

    livewire(ListOrders::class)
        ->callTableAction('cancel', $order, data: [
            'reason' => 'Customer request',
        ])
        ->assertHasNoTableActionErrors();

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

```

For actions that should be **hidden** from certain users, assert visibility separately:

```php
it('hides cancel action for non-admin', function () {
    $this->actingAs(User::factory()->create(['role' => 'viewer']));
    $order = Order::factory()->create();

    livewire(ListOrders::class)
        ->assertTableActionHidden('cancel', $order);
});

```

---

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

Mount the `CreateRecord` or `EditRecord` page and use `fillForm` + `call('create')` or `call('save')`:

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

it('creates an order with valid data', function () {
    livewire(CreateOrder::class)
        ->fillForm([
            'customer_id' => Customer::factory()->create()->id,
            'total'       => 9999,
            'status'      => 'pending',
        ])
        ->call('create')
        ->assertHasNoFormErrors();

    $this->assertDatabaseHas('orders', ['total' => 9999]);
});

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

```

---

Testing Page-Level Actions (Header Actions)
-------------------------------------------

Header actions like "Export" or "Import" live on the page, not the table:

```php
livewire(ListOrders::class)
    ->callAction('export')
    ->assertDispatched('filament.notifications');

```

---

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

- Mount Filament pages with `livewire(PageClass::class)` — no browser required.
- Use `assertCanSeeTableRecords`, `assertTableColumnStateSet`, and `assertCountTableRecords` for table assertions.
- `callTableAction` handles the full action lifecycle including modal forms.
- Assert hidden/disabled actions explicitly for authorization coverage.
- Keep one assertion per test; Pest's `expect()` chaining reads cleanly alongside Livewire assertions.
- Always call `->fresh()` on Eloquent models after mutations — Livewire tests don't refresh your local variable.

 Found this useful?

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

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

  3 questions  

     Q01  Do I need to register the Filament panel in tests, or is the service provider enough?        For most resource tests, booting the FilamentServiceProvider and acting as an authenticated user is sufficient. If your panel uses custom middleware or auth guards, configure those in your TestCase `setUp` or use a dedicated Pest `uses()` group with a `beforeEach` that calls `$this-&gt;actingAs()` with the correct guard. 

      Q02  How do I test a Filament action that dispatches a queued job?        Call `Queue::fake()` before mounting the Livewire component, execute the action with `callTableAction` or `callAction`, then assert with `Queue::assertPushed(YourJob::class)`. This keeps the test fast and decoupled from the job's internals. 

      Q03  Can I test custom Filament form components the same way?        Yes. Custom components that extend Filament's base Field classes are exercised through `fillForm` and `assertHasFormErrors` just like built-in fields. If the component has client-side behaviour, test the PHP contract in Pest and cover the JS separately with a browser test only when necessary. 

  Continue reading

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

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

 [ ![Job Batching with Laravel Horizon: Reliable Async Workflows at Scale](https://cdn.msaied.com/553/b794b736bfd84f3cbcc6218319916544.png) laravel queues horizon 

### Job Batching with Laravel Horizon: Reliable Async Workflows at Scale

Learn how to combine Laravel's job batching API with Horizon's queue supervision to build fault-tolerant async...

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

 15 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/job-batching-with-laravel-horizon-reliable-async-workflows-at-scale) [ ![Contextual Eloquent Casts: Custom Cast Classes, Value Objects, and Inbound-Only Transforms](https://cdn.msaied.com/552/a7825c0c6f53d934f84fce522573eafb.png) laravel eloquent value-objects 

### Contextual Eloquent Casts: Custom Cast Classes, Value Objects, and Inbound-Only Transforms

Go beyond primitive casts. Learn how to build custom Eloquent cast classes that hydrate value objects, handle...

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

 15 Aug 2026     1 min read  

  Read    

 ](https://msaied.com/articles/contextual-eloquent-casts-custom-cast-classes-value-objects-and-inbound-only-transforms) [ ![Contextual Eloquent Scopes: Binding Query Logic to Domain State at Runtime](https://cdn.msaied.com/551/dc00bc1e6fb2999c99a0b5b8fb42a8c3.png) laravel eloquent architecture 

### Contextual Eloquent Scopes: Binding Query Logic to Domain State at Runtime

Learn how to attach runtime-aware query scopes to Eloquent models using the service container, avoiding scatte...

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

 15 Aug 2026     3 min read  

  Read    

 ](https://msaied.com/articles/contextual-eloquent-scopes-binding-query-logic-to-domain-state-at-runtime) 

   [  ![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)
