Skip to content

Pest-Konfiguration und globale Test-Infrastruktur

Diese Dokumentation beschreibt die zentrale Pest-Konfiguration in tests/Pest.php und die globalen Test-Infrastruktur-Komponenten.

Übersicht

Die Pest-Konfiguration definiert:

  1. Test-Scopes mit automatischen Traits (DatabaseTransactions)
  2. Helper-Funktionen für Authentifizierung und Übungen
  3. Custom Expectations für domänenspezifische Assertions
  4. Base-Data User Mapping für konsistente Testdaten

Datei: tests/Pest.php

Test-Scopes und Traits

php
// Feature-Tests: TestCase + DatabaseTransactions
uses(Tests\TestCase::class, WithDatabaseTransactions::class)->in('Feature');

// Unit-Tests: Nur TestCase (keine DB-Transactions)
uses(Tests\TestCase::class)->in('Unit');

Bedeutung:

  • Alle Tests in tests/Feature/ erhalten automatisch DatabaseTransactions
  • Alle Tests in tests/Unit/ haben keine automatischen Rollbacks
  • Modul-Tests (modules/*/*/tests/) müssen uses() lokal definieren

Beispiel Modul-Test:

php
// modules/Uebungen/UebungVorlage/tests/Feature/UebungVorlageAPITest.php
<?php

use Tests\Support\WithDatabaseTransactions;

uses(WithDatabaseTransactions::class);

test('kann übung erstellen', function () {
    // Test-Code
});

Base-Data User Mapping

php
const BASE_DATA_USERS = [
    'admin' => 'bruce.wayne',
    'editor' => 'harvey.dent',
    'tester' => 'james.gordon',
    'teacher' => 'selina.kyle',
    'user' => 'joker',
];

Diese Konstante mappt Rollen auf konkrete Usernamen in der Test-Datenbank.

Wichtig: Die User müssen via php artisan test:setup in der Datenbank existieren!

Globale Helper-Funktionen

Authentifizierung

getBaseDataUser(string $role): User

Holt einen Base-Data User anhand der Rolle.

php
$admin = getBaseDataUser('admin');
expect($admin->username)->toBe('bruce.wayne');

$teacher = getBaseDataUser('teacher');
expect($teacher->role)->toBe('teacher');

Wirft Exception bei:

  • Unbekannter Rolle
  • User nicht in Datenbank gefunden (Setup fehlt)

actingAsRole(string $role): User

Meldet einen Base-Data User an und gibt ihn zurück.

php
$admin = actingAsRole('admin');
// $admin ist jetzt eingeloggt

$response = getJson('/api/admin/dashboard');
expect($response->status())->toBe(200);

Convenience-Funktionen

Für häufig verwendete Rollen gibt es spezifische Funktionen:

php
actingAsAdmin();    // bruce.wayne
actingAsEditor();   // harvey.dent
actingAsTester();   // james.gordon
actingAsTeacher();  // selina.kyle
actingAsUser();     // joker

Verwendung in Tests:

php
test('admin kann dashboard zugreifen', function () {
    actingAsAdmin();

    $response = getJson('/api/admin/dashboard');
    expect($response->status())->toBe(200);
});

test('normale user werden blockiert', function () {
    actingAsUser();

    $response = getJson('/api/admin/dashboard');
    expect($response->status())->toBe(403);
});

Übungs-Helper

getUebungAliases(): array

Gibt alle registrierten Übungs-Aliase aus der UebungenRegistry zurück.

php
$aliases = getUebungAliases();
// ['uebung-bedeutungen-zuordnen', 'uebung-formen-bestimmen', ...]

expect($aliases)->toContain('uebung-bedeutungen-zuordnen');

getRegistryEntry(string $alias): mixed

Gibt den vollständigen Registry-Eintrag einer Übung zurück.

php
$entry = getRegistryEntry('uebung-bedeutungen-zuordnen');

expect($entry)->not->toBeNull();
expect($entry->config)->toHaveKey('options');

Custom Expectations

Pest wurde mit Hermeneus-spezifischen Expectations erweitert.

Lemma/Vokabel-Expectations

toBeValidLemma()

Prüft ob ein Lemma-Objekt valide ist.

Validiert:

  • Lemma ist nicht null
  • Feld lemma ist nicht leer
  • Feld bedeutung ist nicht leer
php
expect($lemma)->toBeValidLemma();

Implementierung:

php
expect()->extend('toBeValidLemma', function () {
    $lemma = $this->value;

    expect($lemma)->not->toBeNull('Lemma sollte nicht null sein');
    expect($lemma->lemma)->not->toBeEmpty('Lemma-Feld sollte nicht leer sein');
    expect($lemma->bedeutung)->not->toBeEmpty('Bedeutung sollte nicht leer sein');

    return $this;
});

toBeMorphable()

Prüft ob ein Lemma morphologisierbar ist.

Validiert:

  • Property IsMorphologisierbar existiert
  • IsMorphologisierbar ist true
php
$verb = Verb::find(1);
expect($verb)->toBeMorphable();

Status-Expectations

toHaveValidStatus()

Prüft ob ein Model einen gültigen Status hat.

Gültige Status:

  • draft
  • pending
  • published
  • archived
php
$uebung = Uebung::find(1);
expect($uebung)->toHaveValidStatus();

Übungs-Expectations

toBeValidUebung()

Prüft ob eine Übung valide ist.

Validiert:

  • Übung ist nicht null
  • Relation base_uebung existiert
  • base_uebung ist Instanz von Uebung
php
$uebung = UebungBedeutungenZuordnen::find(1);
expect($uebung)->toBeValidUebung();

toHaveContentData()

Prüft ob content_data vorhanden ist.

Validiert:

  • Property content_data existiert
  • content_data ist nicht leer
php
$uebung = UebungBedeutungenZuordnen::find(1);
expect($uebung)->toHaveContentData();

toHaveValidGravitas()

Prüft ob das Gravitas-Level valide ist.

Validiert:

  • Property cfg_max_gravitas_level existiert
  • Wert ist zwischen 0 und 100
php
$uebung = Uebung::find(1);
expect($uebung)->toHaveValidGravitas();

Hinweis: Funktioniert sowohl mit Uebung als auch mit spezifischen Übungs-Models.

API-Response-Expectations

toBeSuccessfulApiResponse()

Prüft auf erfolgreiche API-Response.

Validiert:

  • HTTP Status 200
  • Falls success-Feld existiert: success === true
php
$response = postJson('/api/uebungen/store', [...]);
expect($response)->toBeSuccessfulApiResponse();

toHaveApiError(int $expectedStatus = 422)

Prüft auf API-Fehler mit bestimmtem Status-Code.

Default: 422 (Validation Error)

php
$response = postJson('/api/uebungen/store', []);
expect($response)->toHaveApiError(422);

$response = getJson('/api/uebungen/999999');
expect($response)->toHaveApiError(404);

Datasets

Siehe separates Dokument zu Datasets oder Hauptdokumentation.

Kurz:

  • tests/Datasets/users.php - User-Rollen und Base-Data
  • tests/Datasets/vocab.php - Vokabel-bezogene Daten
  • tests/Datasets/uebungen.php - Übungs-bezogene Daten

TestCase.php

Die zentrale Tests\TestCase Klasse definiert:

Setup-Methode

php
protected function setUp(): void
{
    parent::setUp();
    config(['app.url' => 'http://hermeneus.test']);
    URL::forceRootUrl('http://hermeneus.test');
    Schema::enableForeignKeyConstraints();
    $this->disableExceptionHandling();
}

Wichtig:

  • Foreign Keys sind aktiviert (Datenbankintegrität)
  • Exception Handling ist deaktiviert (bessere Fehlerausgabe)

Helper-Methoden

clearTestCache()

Löscht Test-relevante Cache-Keys.

php
test('cache wird geleert', function () {
    $this->clearTestCache();
    // Test-Code
});

Löscht:

  • scope_lookup
  • vocab_all

disableExceptionHandling() / withExceptionHandling()

Deaktiviert/Aktiviert Laravel Exception Handling.

php
test('zeigt rohe exception', function () {
    $this->disableExceptionHandling(); // Default

    // Wirft Exception direkt, kein JSON-Error-Response
    getJson('/api/broken-route');
});

test('nutzt laravel exception handler', function () {
    $this->withExceptionHandling();

    // Gibt JSON-Error-Response zurück
    $response = getJson('/api/broken-route');
    expect($response->status())->toBe(500);
});

Wichtige Hinweise

Base-Data User sind Pflicht

Alle Helper-Funktionen setzen voraus, dass die Test-Datenbank mit Base-Data initialisiert wurde.

Empfohlener Weg (automatisch):

bash
# Intelligenter Test-Runner mit automatischem Setup
npm run test

Der neue test:run Command prüft automatisch, ob die Datenbank bereit ist und führt bei Bedarf ein Setup durch.

Manuelles Setup:

bash
# Nur Setup ohne Tests
php artisan test:setup

# Oder über NPM
npm run test:setup

Bei fehlendem Setup:

php
RuntimeException: Base-Data User 'bruce.wayne' nicht gefunden.
Bitte `php artisan test:setup` ausfuehren.

Siehe auch: TestRunCommand für Details zur Smart-Check-Logik

Module-Tests benötigen explizites uses()

Module-Tests definieren ihre Uses-Statements selbst:

php
// modules/Uebungen/UebungVorlage/tests/Pest.php (optional)
uses(Tests\TestCase::class, WithDatabaseTransactions::class)
    ->in(__DIR__);

// ODER direkt in Test-Datei
uses(WithDatabaseTransactions::class);

Factory vs. Base-Data

VERALTET:

php
$user = User::factory()->create(); // Erzeugt temporären User
$this->actingAs($user);

AKTUELL:

php
actingAsAdmin(); // Nutzt bruce.wayne aus Base-Data

Vorteile Base-Data:

  • Konsistente User-IDs über Tests hinweg
  • Keine temporären Datenbank-Einträge
  • Schnellere Testausführung (kein Factory-Overhead)
  • Realitätsnähere Testdaten

Erweiterte Nutzung

Custom Expectations hinzufügen

Neue Expectations können in tests/Pest.php definiert werden:

php
expect()->extend('toHaveVocabData', function () {
    $uebung = $this->value;

    expect($uebung->content_data)->toHaveKey('vocab_ids');
    expect($uebung->content_data['vocab_ids'])->toBeArray();
    expect($uebung->content_data['vocab_ids'])->not->toBeEmpty();

    return $this;
});

// Verwendung:
expect($uebung)->toHaveVocabData();

Neue Helper-Funktionen

php
function createTestUebung(string $alias): mixed
{
    $entry = getRegistryEntry($alias);
    $modelClass = $entry->modelClass;

    return $modelClass::factory()->create();
}

// Verwendung:
test('uebung wird erstellt', function () {
    $uebung = createTestUebung('uebung-bedeutungen-zuordnen');
    expect($uebung)->toBeValidUebung();
});

Troubleshooting

"Base-Data User nicht gefunden"

bash
php artisan test:setup

"Class 'WithDatabaseTransactions' not found"

php
use Tests\Support\WithDatabaseTransactions;

uses(WithDatabaseTransactions::class);

Tests hinterlassen Datenbank-Artefakte

Prüfe ob uses(WithDatabaseTransactions::class) definiert ist.

"Unknown role: xyz"

Nur folgende Rollen sind definiert:

  • admin
  • editor
  • tester
  • teacher
  • user

Weitere Ressourcen