---
name: testing-conventions
description: How to write PHPUnit Feature/Unit tests for the Mediknode/Unified codebase so they actually run against the multi-subdomain routes with Spatie permissions. Use whenever writing or fixing tests, or when a test 404s/403s unexpectedly.
---

# Testing conventions

The team wants strong coverage — ship a test with every change.

## Skeleton (copy this)
```php
namespace Tests\Feature\Backend;

use App\Models\Event;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Spatie\Permission\Models\Permission;
use Tests\TestCase;

class FooControllerTest extends TestCase
{
    use RefreshDatabase;

    private string $baseUrl;
    private User $admin;
    private Event $event;

    protected function setUp(): void
    {
        parent::setUp();
        $this->baseUrl = 'http://'.config('app.backend_sub_domain').'.'.config('app.domain');

        $this->admin = User::factory()->create();
        $perm = Permission::firstOrCreate(['name' => 'participant_view', 'guard_name' => 'web']);
        $this->admin->givePermissionTo($perm);

        $this->event = Event::factory()->create();
        $this->event->users()->attach($this->admin->id);
    }

    private function asAdmin(): static { return $this->actingAs($this->admin); }

    public function test_liste_retourne_200(): void
    {
        $this->asAdmin()->get($this->baseUrl.'/events/'.$this->event->id.'/participants')
             ->assertStatus(200);
    }
}
```

## Rules
- `use RefreshDatabase;` always.
- **Build the URL from config** (subdomain + domain) — a bare `/path` 404s. Frontend tests use the frontend subdomain.
- Seed Spatie permissions with `firstOrCreate(... 'guard_name' => 'web')`, then `givePermissionTo`. Grep the controller/middleware for exact permission names.
- Test method names in **French**, snake_case.
- Cover the unauthorized (403) and unauthenticated (redirect/login) cases for every backend route.
- Factories exist for `User`, `Event`, `Participant`. Attach event↔user via `$event->users()->attach($user->id)`.
- Models fire observers (audit) and use the Meta pattern — expect extra DB rows.
- Livewire: use `Livewire::test(Component::class)`.

## Priority paths (start coverage here)
payments / `total_price` / vouchers (money), imports (dedup + checks), Form Request validation, RBAC, regression tests for fixed bugs.

## Run
`php artisan test --filter <TestName>` — never weaken an assertion just to make it green; if it caught a real bug, report the bug.

See also: [[routing-multisubdomain]], [[project-conventions]], [[audit-observers]].
