Testing Filament v3 with Pest: Resources &amp; Forms | 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)    Filament v3 Testing with Pest: Resources, Actions, and Form Assertions        On this page       1. [  Why Filament Testing Deserves Its Own Strategy ](#why-filament-testing-deserves-its-own-strategy)
2. [  Setting the Auth Context ](#setting-the-auth-context)
3. [  Asserting Table Columns and Records ](#asserting-table-columns-and-records)
4. [  Testing Table Actions ](#testing-table-actions)
5. [  Form Submission on Create/Edit Pages ](#form-submission-on-createedit-pages)
6. [  Testing Authorization Inside Resources ](#testing-authorization-inside-resources)
7. [  Practical Takeaways ](#practical-takeaways)

  ![Filament v3 Testing with Pest: Resources, Actions, and Form Assertions](https://cdn.msaied.com/177/7c53cf56fda3d488b9f618ccf3998a78.png)

  #filament   #pest   #testing   #laravel   #livewire  

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

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

       Table of contents

1. [  01   Why Filament Testing Deserves Its Own Strategy  ](#why-filament-testing-deserves-its-own-strategy)
2. [  02   Setting the Auth Context  ](#setting-the-auth-context)
3. [  03   Asserting Table Columns and Records  ](#asserting-table-columns-and-records)
4. [  04   Testing Table Actions  ](#testing-table-actions)
5. [  05   Form Submission on Create/Edit Pages  ](#form-submission-on-createedit-pages)
6. [  06   Testing Authorization Inside Resources  ](#testing-authorization-inside-resources)
7. [  07   Practical Takeaways  ](#practical-takeaways)

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

Filament panels are Livewire components under the hood. That means standard HTTP tests miss most of what matters — form validation, action modals, bulk operations, and authorization checks all live inside component state. Pest's Livewire integration, combined with Filament's own test helpers, gives you a clean path to full coverage without brittle browser tests.

---

Setting the Auth Context
------------------------

Every Filament resource test needs an authenticated panel user. Use `actingAs` before mounting the component, and make sure the user satisfies your panel's `auth()->check()` guard.

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

it('renders the orders list for an admin', function () {
    $admin = User::factory()->admin()->create();

    livewire(ListOrders::class)
        ->actingAs($admin)
        ->assertSuccessful();
});

```

If your panel uses a custom guard, pass it explicitly: `actingAs($admin, 'admin')`.

---

Asserting Table Columns and Records
-----------------------------------

Filament's `InteractsWithTable` trait exposes helpers that map directly to what the user sees.

```php
it('shows pending orders in the table', function () {
    $admin = User::factory()->admin()->create();
    Order::factory()->pending()->count(3)->create();
    Order::factory()->completed()->count(2)->create();

    livewire(ListOrders::class)
        ->actingAs($admin)
        ->assertCountTableRecords(3); // default filter: pending
});

```

Use `->assertCanSeeTableRecords($records)` when you need to assert specific model instances appear, and `->assertCanNotSeeTableRecords($records)` for scoping and authorization checks.

---

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

Row actions and bulk actions are where most bugs hide. Filament provides `callTableAction` and `callTableBulkAction`.

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

    livewire(ListOrders::class)
        ->actingAs($admin)
        ->callTableAction('mark_shipped', $order)
        ->assertHasNoTableActionErrors();

    expect($order->fresh()->status)->toBe(OrderStatus::Shipped);
});

```

For actions that open a modal with a form, pass data as the third argument:

```php
->callTableAction('reassign', $order, data: [
    'assignee_id' => $agent->id,
])
->assertHasNoTableActionErrors();

```

---

Form Submission on Create/Edit Pages
------------------------------------

Mount the `CreateOrder` or `EditOrder` page directly and use `fillForm` + `call('create')` or `call('save')`.

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

it('validates required fields on create', function () {
    $admin = User::factory()->admin()->create();

    livewire(CreateOrder::class)
        ->actingAs($admin)
        ->fillForm([
            'customer_id' => null,
            'total' => -10,
        ])
        ->call('create')
        ->assertHasFormErrors([
            'customer_id' => 'required',
            'total' => 'min',
        ]);
});

it('creates an order with valid data', function () {
    $admin = User::factory()->admin()->create();
    $customer = Customer::factory()->create();

    livewire(CreateOrder::class)
        ->actingAs($admin)
        ->fillForm([
            'customer_id' => $customer->id,
            'total' => 150.00,
            'status' => OrderStatus::Pending->value,
        ])
        ->call('create')
        ->assertHasNoFormErrors();

    $this->assertDatabaseHas('orders', [
        'customer_id' => $customer->id,
        'total' => 150.00,
    ]);
});

```

---

Testing Authorization Inside Resources
--------------------------------------

Filament respects policies. Test that unauthorized users are blocked at the component level, not just the route.

```php
it('prevents non-admins from deleting orders', function () {
    $viewer = User::factory()->viewer()->create();
    $order = Order::factory()->create();

    livewire(ListOrders::class)
        ->actingAs($viewer)
        ->assertTableActionHidden('delete', $order);
});

```

`assertTableActionHidden` checks that the action is not rendered for that record — a stronger guarantee than checking HTTP status codes.

---

Practical Takeaways
-------------------

- Always call `actingAs` before mounting; Filament panels enforce auth at the component level.
- Use `assertCountTableRecords` for scope/filter tests — it respects the component's default filters.
- `callTableAction` with a `data` array covers modal-based actions without browser overhead.
- `assertHasFormErrors` accepts field-rule pairs for precise validation assertions.
- Test authorization with `assertTableActionHidden` / `assertTableActionVisible` rather than policy unit tests alone.
- Keep each test focused on one behavior; Filament component state resets between Pest tests automatically.

 Found this useful?

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

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

  3 questions  

     Q01  Do I need to register the Filament panel in tests?        Yes. Filament resolves resources through the panel registry, so your test environment must boot the panel. Typically this happens automatically via your AppServiceProvider or a dedicated test service provider. If resources are not found, ensure the panel's `boot` method runs during the test suite. 

      Q02  How do I test a custom Filament action that dispatches a job?        Fake the queue with `Queue::fake()` before calling `callTableAction`, then assert the job was pushed with `Queue::assertPushed(MyJob::class)`. The action executes synchronously inside the Livewire test, so the fake captures the dispatch correctly. 

      Q03  Can I test Filament notifications triggered by actions?        Yes. After calling an action, chain `-&gt;assertNotified()` or `-&gt;assertNotified('Order shipped')` to assert that a Filament notification was dispatched to the current session. This works for both success and error notifications defined inside your action's `action()` closure. 

  Continue reading

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

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

 [ ![Filament v4 Custom Field Plugins: Building Reusable Schema Components](https://cdn.msaied.com/179/3d535cc5bcced4a170d41e383ab06883.png) filament laravel filament-v4 

### Filament v4 Custom Field Plugins: Building Reusable Schema Components

Learn how to build a distributable Filament v4 custom field plugin using the unified Schema API, auto-discover...

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

 15 Jun 2026     1 min read  

  Read    

 ](https://msaied.com/articles/filament-v4-custom-field-plugins-building-reusable-schema-components) [ ![Laravel Octane Worker Lifecycle, State Leakage, and Memory Management](https://cdn.msaied.com/178/ad8e002dedd4857edb32e66305f47498.png) laravel octane performance 

### Laravel Octane Worker Lifecycle, State Leakage, and Memory Management

Octane keeps workers alive between requests, which means singleton state, static properties, and unbounded cac...

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

 15 Jun 2026     3 min read  

  Read    

 ](https://msaied.com/articles/laravel-octane-worker-lifecycle-state-leakage-and-memory-management) [ ![Recursive Queries in MySQL 8 with Laravel: CTEs, Hierarchies, and Adjacency Lists](https://cdn.msaied.com/176/e761e34c4eeac562198091ba035908ca.png) laravel mysql database 

### Recursive Queries in MySQL 8 with Laravel: CTEs, Hierarchies, and Adjacency Lists

MySQL 8 supports recursive CTEs. Learn how to query adjacency-list hierarchies in Laravel without pulling the...

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

 14 Jun 2026     2 min read  

  Read    

 ](https://msaied.com/articles/recursive-queries-in-mysql-8-with-laravel-ctes-hierarchies-and-adjacency-lists) 

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