Testing Filament 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 Tests Are Worth the Effort ](#why-filament-tests-are-worth-the-effort)
2. [  Project Setup ](#project-setup)
3. [  Testing a Resource List Page ](#testing-a-resource-list-page)
4. [  Asserting Table Actions ](#asserting-table-actions)
5. [  Form Field Assertions on Create/Edit Pages ](#form-field-assertions-on-createedit-pages)
6. [  Testing Authorization Inside Filament ](#testing-authorization-inside-filament)
7. [  Infolist Assertions (Filament v4) ](#infolist-assertions-filament-v4)
8. [  Key Takeaways ](#key-takeaways)

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

  #filament   #pest   #testing   #laravel  

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

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

       Table of contents

1. [  01   Why Filament Tests Are Worth the Effort  ](#why-filament-tests-are-worth-the-effort)
2. [  02   Project Setup  ](#project-setup)
3. [  03   Testing a Resource List Page  ](#testing-a-resource-list-page)
4. [  04   Asserting Table Actions  ](#asserting-table-actions)
5. [  05   Form Field Assertions on Create/Edit Pages  ](#form-field-assertions-on-createedit-pages)
6. [  06   Testing Authorization Inside Filament  ](#testing-authorization-inside-filament)
7. [  07   Infolist Assertions (Filament v4)  ](#infolist-assertions-filament-v4)
8. [  08   Key Takeaways  ](#key-takeaways)

 Why Filament Tests Are Worth the Effort
---------------------------------------

Filament panels generate a lot of implicit behaviour — authorization checks, form validation, table bulk actions, and page redirects — all wired together through Livewire. Skipping tests means every refactor is a gamble. The good news: Filament ships with first-class Pest/PHPUnit helpers that let you drive the entire panel through HTTP and Livewire assertions, no Dusk required.

Project Setup
-------------

Install the testing helpers if you haven't already:

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

```

Filament's test helpers live in `Filament\Testing` and are available on the `livewire()` helper once the panel is booted. Add a base `TestCase` that boots your panel:

```php
// tests/TestCase.php
protected function setUp(): void
{
    parent::setUp();
    $this->actingAs(User::factory()->admin()->create());
}

```

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

The simplest assertion confirms the page renders and shows records:

```php
use App\Filament\Resources\OrderResource;
use App\Models\Order;
use function Pest\Laravel\get;

it('renders the order list page', function () {
    Order::factory()->count(3)->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->assertCanSeeTableRecords(Order::all());
});

```

`assertCanSeeTableRecords` checks that the Livewire component's table query returns those models — it does **not** assert rendered HTML, so it stays fast.

Asserting Table Actions
-----------------------

Table row actions and bulk actions each have dedicated helpers:

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

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

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

it('bulk-deletes selected orders', function () {
    $orders = Order::factory()->count(5)->create();

    livewire(OrderResource\Pages\ListOrders::class)
        ->callTableBulkAction('delete', $orders)
        ->assertHasNoTableActionErrors();

    expect(Order::count())->toBe(0);
});

```

`callTableAction` accepts the action name (the string you pass to `Action::make()`) and the target record. If the action opens a modal form, pass data as the third argument.

Form Field Assertions on Create/Edit Pages
------------------------------------------

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

    expect(Order::count())->toBe(1);
});

it('fails validation when total is negative', function () {
    livewire(OrderResource\Pages\CreateOrder::class)
        ->fillForm(['total' => -10])
        ->call('create')
        ->assertHasFormErrors(['total' => 'min']);
});

```

`assertHasFormErrors` accepts either a plain field name or a `field => rule` pair, matching Laravel's validation key format.

Testing Authorization Inside Filament
-------------------------------------

Filament respects `canCreate`, `canEdit`, and `canDelete` policy methods. Test them explicitly:

```php
it('hides the create button for viewers', function () {
    $this->actingAs(User::factory()->viewer()->create());

    livewire(OrderResource\Pages\ListOrders::class)
        ->assertActionHidden('create');
});

```

This prevents silent authorization regressions when policies change.

Infolist Assertions (Filament v4)
---------------------------------

Filament v4 infolists are also testable:

```php
it('displays the order total in the infolist', function () {
    $order = Order::factory()->create(['total' => 250.00]);

    livewire(OrderResource\Pages\ViewOrder::class, ['record' => $order->id])
        ->assertInfolists()
        ->assertInfolists(['total' => '$250.00']);
});

```

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

- Use `livewire(PageClass::class)` — never raw HTTP — for Filament page tests.
- `assertCanSeeTableRecords` checks query results, not DOM; it's fast and reliable.
- Pass action data as the third argument to `callTableAction` for modal-driven actions.
- `assertHasFormErrors(['field' => 'rule'])` catches specific validation failures cleanly.
- Always test authorization explicitly; policy regressions are silent without it.
- Filament v4 infolist helpers follow the same pattern as form helpers.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fmsaied.com%2Farticles%2Ftesting-filament-resources-actions-and-form-assertions-with-pest-2&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-2) 

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

  3 questions  

     Q01  Do Filament Pest tests require Laravel Dusk or a real browser?        No. Filament's test helpers drive the Livewire components directly over HTTP, so tests run in-process and are much faster than browser-based tests. 

      Q02  How do I test a table action that opens a modal form?        Pass the form data as the third argument to `callTableAction('action_name', $record, ['field' =&gt; 'value'])`. Then assert with `assertHasNoTableActionErrors()` or check the database state. 

      Q03  Are these helpers compatible with both Filament v3 and v4?        Most helpers — `callTableAction`, `fillForm`, `assertHasFormErrors` — work in both versions. Filament v4 adds infolist-specific assertions; check the version's changelog for any renamed methods. 

  Continue reading

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

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

 [ ![PostgreSQL CTEs, Window Functions, and Lateral Joins in Laravel](https://cdn.msaied.com/363/8e5685c14467b502c2bcbd62b4f47f64.png) laravel postgresql query-builder 

### PostgreSQL CTEs, Window Functions, and Lateral Joins in Laravel

Go beyond basic Eloquent queries: learn how to harness PostgreSQL CTEs, window functions, and LATERAL joins di...

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

 4 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/postgresql-ctes-window-functions-and-lateral-joins-in-laravel-2) [ ![Eloquent Query Scopes: Global, Local, and Dynamic Scopes Without the Magic Tax](https://cdn.msaied.com/362/ecd807763e4e5019ee04875ba59dc8bc.png) laravel eloquent database 

### Eloquent Query Scopes: Global, Local, and Dynamic Scopes Without the Magic Tax

Query scopes are one of Eloquent's most misused features. This guide shows how to write global, local, and dyn...

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

 4 Jul 2026     3 min read  

  Read    

 ](https://msaied.com/articles/eloquent-query-scopes-global-local-and-dynamic-scopes-without-the-magic-tax) [ ![FrankenPHP, OPcache JIT, and Preloading: Squeezing Real Throughput from Laravel](https://cdn.msaied.com/361/fc51b795acf24849e543d4f941b850a2.png) laravel frankenphp php 

### FrankenPHP, OPcache JIT, and Preloading: Squeezing Real Throughput from Laravel

A practical guide to running Laravel under FrankenPHP with OPcache JIT and preloading enabled — covering worke...

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

 4 Jul 2026     1 min read  

  Read    

 ](https://msaied.com/articles/frankenphp-opcache-jit-and-preloading-squeezing-real-throughput-from-laravel-1) 

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