Testing Filament with Pest: Resources &amp; Actions | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://msaied.com) - [Home](https://msaied.com)
- [Projects](https://msaied.com/projects)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/certificates)
- [About](https://msaied.com#about)

           [  Contact](https://msaied.com#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://msaied.com)
- [ProjectsCase studies](https://msaied.com/projects)
- [ArticlesEngineering notes](https://msaied.com/articles)
- [CertificatesCredentials](https://msaied.com/certificates)
- [AboutHow I work](https://msaied.com#about)
- [ContactGet in touch](https://msaied.com#contact)

  [Start a conversation](https://msaied.com#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://msaied.com)
2. /
3. [Articles](https://msaied.com/articles)
4. /
5. Testing Filament Resources, Actions, and Form Assertions with Pest

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

 Filament panels are notoriously under-tested. This guide shows how to write precise, fast Pest tests for resources, table actions, and form field assertions without spinning up a browser.

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

ShareCopy linkCopied

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

  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)

 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.

- [filament](https://msaied.com/articles?search=filament)
- [pest](https://msaied.com/articles?search=pest)
- [testing](https://msaied.com/articles?search=testing)
- [laravel](https://msaied.com/articles?search=laravel)

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

  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.

   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.

   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.

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

[Mohamed Said](https://msaied.com#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://msaied.com#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 articlePostgreSQL CTEs, Window Functions, and Lateral Joins in Laravel](https://msaied.com/articles/postgresql-ctes-window-functions-and-lateral-joins-in-laravel-2) [Next articleLivewire v3 Performance and State Management Patterns](https://msaied.com/articles/livewire-v3-performance-and-state-management-patterns)  

   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)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://msaied.com#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/731/7cabd03b86a18f1db2c9ff7de1510270.png)  · 3 min read### Laravel Octane + FrankenPHP: Request Lifecycle, Shared State, and Safe Singleton Patterns

3 Oct 2026 ](https://msaied.com/articles/laravel-octane-frankenphp-request-lifecycle-shared-state-and-safe-singleton-patterns) [ ![](https://cdn.msaied.com/728/b96439f5ef5f084b44d736951875f964.png)  · 3 min read### Eloquent Custom Relations: Polymorphic Pivots, Has-One-Of-Many, and Raw Join Relations

2 Oct 2026 ](https://msaied.com/articles/eloquent-custom-relations-polymorphic-pivots-has-one-of-many-and-raw-join-relations) [ ![](https://cdn.msaied.com/727/7873d7495db459e7a6e3bfbb8835852d.png)  · 3 min read### Laravel Caching Strategies: Tags, Stampede Prevention, and Cache-Aside at Scale

2 Oct 2026 ](https://msaied.com/articles/laravel-caching-strategies-tags-stampede-prevention-and-cache-aside-at-scale-2) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://msaied.com#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://msaied.com)
- [Articles](https://msaied.com/articles)
- [Certificates](https://msaied.com/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://msaied.com/sitemap.xml)
