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

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

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

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Testing Filament Resources, Actions, and Form Assertions with Pest        On this page       1. [  Why Testing Filament Deserves Its Own Strategy ](#why-testing-filament-deserves-its-own-strategy)
2. [  Project Setup ](#project-setup)
3. [  Testing a List Page ](#testing-a-list-page)
4. [  Filtering and Searching ](#filtering-and-searching)
5. [  Testing Table Actions ](#testing-table-actions)
6. [  Testing Create and Edit Forms ](#testing-create-and-edit-forms)
7. [  Edit Page — Pre-filled State ](#edit-page-pre-filled-state)
8. [  Authorization Inside Tests ](#authorization-inside-tests)
9. [  Key Takeaways ](#key-takeaways)

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

  #filament   #pest   #testing   #laravel  

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

     11 Sep 2026      4 min read    ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)  Mohamed Said  

       Table of contents

  9 sections  

1. [  01   Why Testing Filament Deserves Its Own Strategy  ](#why-testing-filament-deserves-its-own-strategy)
2. [  02   Project Setup  ](#project-setup)
3. [  03   Testing a List Page  ](#testing-a-list-page)
4. [  04   Filtering and Searching  ](#filtering-and-searching)
5. [  05   Testing Table Actions  ](#testing-table-actions)
6. [  06   Testing Create and Edit Forms  ](#testing-create-and-edit-forms)
7. [  07   Edit Page — Pre-filled State  ](#edit-page-pre-filled-state)
8. [  08   Authorization Inside Tests  ](#authorization-inside-tests)
9. [  09   Key Takeaways  ](#key-takeaways)

       Why Testing Filament Deserves Its Own Strategy
----------------------------------------------

Filament ships a first-class testing API built on top of Livewire's testing utilities. Most teams either skip it entirely or write brittle browser tests. Neither is acceptable when a resource manages production data. The goal here is fast, deterministic Pest tests that cover the paths users actually hit.

### Project Setup

Install the Filament testing package alongside Pest:

```bash
composer require --dev filament/filament pestphp/pest-plugin-laravel

```

Filament's helpers live in `Filament\Tests` and are pulled in automatically when you use `livewire()` inside a test. No extra trait is needed beyond `RefreshDatabase`.

---

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

The list page is the most common entry point. Assert that the table renders and that records appear:

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

it('renders the order list page', function () {
    $this->actingAs(User::factory()->admin()->create());

    $orders = Order::factory(3)->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->assertCanSeeTableRecords($orders);
});

```

`assertCanSeeTableRecords` checks that the Livewire component's rendered HTML contains each model's row — it does **not** fire a real browser.

### Filtering and Searching

```php
it('filters orders by status', function () {
    $pending = Order::factory()->pending()->create();
    $shipped = Order::factory()->shipped()->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->filterTable('status', 'pending')
        ->assertCanSeeTableRecords([$pending])
        ->assertCanNotSeeTableRecords([$shipped]);
});

```

The `filterTable($filterName, $value)` helper maps directly to the filter key you registered on the table.

---

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

Bulk actions and row actions are where bugs hide. Test them explicitly:

```php
it('marks an order as shipped via row action', function () {
    $order = Order::factory()->pending()->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->callTableAction('mark_shipped', $order)
        ->assertHasNoTableActionErrors();

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

```

For actions that open a modal with a form, fill the fields before calling:

```php
it('assigns a courier via action modal', function () {
    $order = Order::factory()->create();
    $courier = Courier::factory()->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->mountTableAction('assign_courier', $order)
        ->setTableActionData(['courier_id' => $courier->id])
        ->callMountedTableAction()
        ->assertHasNoTableActionErrors();

    expect($order->fresh()->courier_id)->toBe($courier->id);
});

```

---

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

Form assertions validate that Filament's schema maps correctly to your model:

```php
it('creates an order with valid data', function () {
    livewire(OrderResource\Pages\CreateOrder::class)
        ->fillForm([
            'customer_id' => Customer::factory()->create()->id,
            'notes'       => 'Rush delivery',
            'status'      => 'pending',
        ])
        ->call('create')
        ->assertHasNoFormErrors();

    $this->assertDatabaseHas('orders', ['notes' => 'Rush delivery']);
});

it('fails validation when customer is missing', function () {
    livewire(OrderResource\Pages\CreateOrder::class)
        ->fillForm(['notes' => 'No customer'])
        ->call('create')
        ->assertHasFormErrors(['customer_id' => 'required']);
});

```

### Edit Page — Pre-filled State

```php
it('pre-fills the edit form with existing data', function () {
    $order = Order::factory()->create(['notes' => 'Original note']);

    livewire(OrderResource\Pages\EditOrder::class, ['record' => $order->getRouteKey()])
        ->assertFormSet(['notes' => 'Original note']);
});

```

`assertFormSet` is the quickest way to verify that `fill()` inside `mount()` works correctly.

---

Authorization Inside Tests
--------------------------

Don't forget to test that unauthorized users are blocked:

```php
it('prevents non-admins from deleting orders', function () {
    $this->actingAs(User::factory()->create()); // no admin role
    $order = Order::factory()->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->assertTableActionHidden('delete', $order);
});

```

`assertTableActionHidden` confirms the action is not rendered for that record — no need to call it and catch an exception.

---

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

- Use `livewire(PageClass::class)` — Filament pages are Livewire components and test identically.
- `filterTable`, `callTableAction`, and `fillForm` cover 90% of real user paths.
- Always assert the **database state** after mutations, not just the absence of errors.
- Test authorization with `assertTableActionHidden` / `assertTableActionVisible` rather than role-checking in isolation.
- Keep each test focused on one behavior; share fixture setup via Pest `beforeEach` or dataset factories.

 Found this useful?

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

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

  3 questions  

     Q01  Do I need to publish Filament's test helpers or install a separate package?        No. Filament's testing helpers are bundled with the main `filament/filament` package. As long as you have Pest and the Laravel plugin installed, you can call `livewire()` directly on any Filament page class inside your tests. 

      Q02  How do I test a custom action that dispatches a job or fires an event?        Wrap the action call with `Bus::fake()` or `Event::fake()` before mounting the Livewire component, then call `callTableAction()` or `callMountedTableAction()` as normal. Assert the job or event was dispatched after the call completes. 

      Q03  Can these tests run in CI without a database?        They require a database because Filament queries Eloquent models. Use SQLite in-memory (`:memory:`) in your `phpunit.xml` for fast CI runs, combined with `RefreshDatabase` to keep each test isolated. 

  Continue reading

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

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

 [ ![Referenceable: Generate Clean and Customizable Reference Numbers in Laravel](https://cdn.msaied.com/656/01M27ZF9VSQC7BQC96HV2KABSD.png) 

### Referenceable: Generate Clean and Customizable Reference Numbers in Laravel

Referenceable is an open-source Laravel package for generating clean, customizable reference numbers for Eloqu...

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

 11 Sep 2026     8 min read  

  Read    

 ](https://www.msaied.com/articles/referenceable-generate-clean-and-customizable-reference-numbers-in-laravel) [ ![Bifrost Turns One: AI Builds, MCP Server, and Automated Workflows for NativePHP](https://cdn.msaied.com/653/e105bc3450f63955d42ed8feeb5a60f7.png) NativePHP Bifrost MCP 

### Bifrost Turns One: AI Builds, MCP Server, and Automated Workflows for NativePHP

Bifrost, the NativePHP build and distribution service, celebrates its first anniversary and 10,000 builds with...

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

 10 Sep 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/bifrost-turns-one-ai-builds-mcp-server-and-automated-workflows-for-nativephp) [ ![Domain-Driven Design in Laravel: Actions, DTOs, and Value Objects Without Bloat](https://cdn.msaied.com/651/39fb698517dd3f400bfae2ee03b70879.png) laravel ddd clean-architecture 

### Domain-Driven Design in Laravel: Actions, DTOs, and Value Objects Without Bloat

Skip the ceremony. Learn how to apply DDD's most practical building blocks—Actions, DTOs, and Value Objects—in...

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

 10 Sep 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/domain-driven-design-in-laravel-actions-dtos-and-value-objects-without-bloat-3) 

   [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.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://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.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)
