# Testing tables

> Assert table search, row identity, and result totals in a Laravel feature test.

Source: https://docs.inertiax.dev/table/guide/testing/

Test the table response through its application route. This checks the query, table definition, and Inertia prop together without relying on rendered HTML.

## Assert a searched result

This test runs against the Invoices table in the [agent guide](https://docs.inertiax.dev/table/agent-guide/). Its `/invoices` route returns an `invoices` table prop that keeps USD invoices and can search, sort and filter them. Replace the route, table name and models with your application’s own. Omit `uses(...)` if `tests/Pest.php` already applies it, as Laravel’s starter kits do for `tests/Feature`.

```php
use App\Models\Customer;
use App\Models\Invoice;
use Illuminate\Foundation\Testing\RefreshDatabase;
use InertiaX\Table\Testing\AssertableTable;
use InertiaX\Table\Testing\TableRequest;
use Tests\TestCase;

uses(TestCase::class, RefreshDatabase::class);

test('invoice search, sort and filter', function () {
    $customer = Customer::factory()->create(['name' => 'Acme']);
    $invoices = Invoice::factory()->for($customer)->state([
        'currency' => 'USD', 'total_cents' => 12500, 'status' => 'paid',
    ]);
    $first = $invoices->create(['reference' => 'INV-001']);
    $last = $invoices->create(['reference' => 'INV-002']);
    $first->lines()->createMany([['description' => 'Design'], ['description' => 'Review']]);
    $invoices->create(['reference' => 'INV-EUR', 'currency' => 'EUR']);
    $invoices->create(['reference' => 'INV-DRAFT', 'status' => 'draft']);
    $invoices->for(Customer::factory()->state(['name' => 'Other']))
        ->create(['reference' => 'INV-OTHER']);
    $url = TableRequest::url('/invoices', 'invoices',
        sort: '-reference', filters: ['status' => 'paid'], search: 'acme');

    AssertableTable::fromResponse($this->get($url), 'invoices')
        ->hasRows(2)->hasTotal(2)->hasRowIds([$last->id, $first->id])
        ->hasState('search', 'acme')
        ->hasCellValue($first->id, 'customer_name', 'Acme')
        ->hasCellValue($first->id, 'line_count', 2)
        ->hasCellValue($first->id, 'total_cents', 12500);
});
```

The EUR invoice checks the query scope, the draft invoice checks the status filter, and the other customer’s invoice checks search exclusion. Asserting the IDs in order checks the sort and catches wrong rows even when the total happens to be correct. `TableRequest::url()` builds the table’s URL state; see [URL state](https://docs.inertiax.dev/table/guide/url-state/). See [Testing API](https://docs.inertiax.dev/table/reference/testing/) for all assertions and comparison rules.

## Assert invalid links

```php
AssertableTable::fromResponse($this->get('/invoices?invoices_filters=garbage'), 'invoices')
    ->hasProblem('filters')
    ->allowsReset();
```

Use your application’s actual route and table name. Test authorization, query scoping, and the records returned by valid filters through that route as well.

## Choose the assertion boundary

PHP assertions check transmitted values. A currency cell containing `8419` does not contain the browser text `$84.19`; use a browser test for formatted text, visible controls, and accessibility. Use ordinary `assertInertia` assertions when checking additional metadata such as `numberFormat`.

For API/repository retrieval, also test your [custom source](https://docs.inertiax.dev/table/guide/custom-sources/) with a `TableQuery`. See [URL state](https://docs.inertiax.dev/table/guide/url-state/) for constructing sort and filter links.
