Testing Strategy
All tests are written with Pest PHP. PHPUnit is the underlying runner. Pest adds an expressive, function-based syntax that reduces boilerplate and makes test intent clear.
Three Test Layers
| Layer | Location | Tool | What it tests |
|---|---|---|---|
| API feature tests | tests/Feature/Api/ | Pest + actingAs() | HTTP endpoints end-to-end |
| Filament tests | tests/Feature/Filament/ | Pest + Livewire test helpers | Panel resources, actions, filters |
| Unit tests | tests/Unit/ | Pest | Domain services in isolation |
Conventions
Every test runs in a database transaction. The RefreshDatabase trait rolls back
after each test — no test pollution between runs.
Every test establishes a tenant. The first line of every feature test creates a
LawFirm and sets it as the Filament tenant:
beforeEach(function () {
$this->firm = LawFirm::factory()->create();
$this->actingAsPanel($this->firm); // custom helper — sets tenant + auth guard
});
Three user factory helpers match the three user types:
staffUser(role: 'LAWYER') // creates User + FirmUserProfile + UserRoleAssignment
clientUser() // creates User + ClientUser + ClientContact
partnerUser() // creates User + PartnerAttorneyUser + PartnerAttorney
Every endpoint is tested for five standard cases:
| Case | Expected |
|---|---|
| Happy path (authenticated + authorized) | 200 / 201 / 204 |
| Unauthenticated | 401 |
| Wrong role (authenticated, insufficient permission) | 403 |
| Cross-tenant access (valid user, wrong firm) | 403 |
| Invalid input | 422 |
Running Tests
# All tests
php artisan test
# Feature tests only
php artisan test --testsuite=Feature
# Single file
php artisan test tests/Feature/Api/Cases/CaseCreateTest.php
# With coverage (requires Xdebug or PCOV)
php artisan test --coverage --min=80
Test File Structure
tests/
├── Feature/
│ ├── Api/ # REST endpoint tests (see api-tests.md)
│ ├── Admin/ # Admin API tests (see admin-api-tests.md)
│ └── Filament/ # Panel resource tests (see filament-tests.md)
├── Unit/
│ ├── Services/ # Domain service tests (see service-unit-tests.md)
│ └── Models/
│ └── AppendOnlyModelTest.php
├── Datasets/
│ ├── users.php
│ ├── matters.php
│ └── billing.php
└── Helpers/
└── PanelTestHelper.php # actingAsPanel(), staffUser(), etc.
Coverage Targets
| Layer | Target |
|---|---|
Domain services (app/Services/) | 95%+ |
API controllers (app/Http/Controllers/Api/) | 90%+ |
Policies (app/Policies/) | 90%+ |
| Filament resources | 70%+ |
| Overall | 80%+ |
Financial logic (RateResolutionService, InvoiceGeneratorService) must have 100%
branch coverage — every code path exercised by a test.