Testing Filament v3 with Pest: Resources &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. Testing Filament Resources, Actions, and Form Assertions with Pest

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

 Learn how to write precise, maintainable Pest tests for Filament v3 resources — covering table actions, form submissions, and authorization assertions without brittle selectors.

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

ShareCopy linkCopied

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

  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)

 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.

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

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

  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.

   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.

   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.

   ![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 articleContextual DTOs and Value Objects in Laravel Without the Bloat](https://www.msaied.com/public/articles/contextual-dtos-and-value-objects-in-laravel-without-the-bloat) [Next articleContextual Eloquent Scopes: Binding Query Logic to Domain State at Runtime](https://www.msaied.com/public/articles/contextual-eloquent-scopes-binding-query-logic-to-domain-state-at-runtime)  

   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)

 ###  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/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  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)
