Funktionale Tests mit WebTestCase
Funktionale Tests mit WebTestCase
~16 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Unit-Tests aus Kapitel 42 prüfen ISOLIERTE Logik – jetzt testen wir das ZUSAMMENSPIEL: eine ECHTE HTTP-Anfrage an /login, die durch Routing, Security UND Controller läuft.
Die WebTestCase-Basisklasse
<?php
declare(strict_types=1);
namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class SecurityControllerTest extends WebTestCase
{
public function testLoginSeiteLaedt(): void
{
$client = static::createClient();
$client->request('GET', '/login');
self::assertResponseIsSuccessful();
self::assertSelectorExists('form');
}
}createClient() erzeugt einen simulierten HTTP-Client, der GEGEN eine TATSÄCHLICH gebootete Symfony-Anwendung testet (in der test-Umgebung aus Kapitel 4) – request() löst eine ECHTE Anfrage aus, die durch Routing (Kapitel 7), Security (Block 5) UND den Controller läuft.
Wichtige Assertions für funktionale Tests
assertResponseIsSuccessful()– Status-Code 2xx.assertResponseStatusCodeSame(404)– ein KONKRETER Status-Code.assertResponseRedirects('/login')– Weiterleitung zu einer bestimmten Route.assertSelectorExists('form')– ein CSS-Selektor findet ein Element im gerenderten HTML.assertSelectorTextContains('h1', 'Anmelden')– ein Element enthält bestimmten Text.
Ein Formular ausfüllen und absenden
public function testRegistrierungFunktioniert(): void
{
$client = static::createClient();
$crawler = $client->request('GET', '/register');
$client->submitForm('Registrieren', [
'registration_form[name]' => 'Test-Nutzer',
'registration_form[email]' => 'test@example.com',
'registration_form[plainPassword]' => 'sicheres-passwort-123',
]);
self::assertResponseRedirects('/login');
}submitForm('Registrieren', [...]) findet den Button mit dem Text "Registrieren" (übersetzt aus dem Submit-Button des Formulars), füllt die angegebenen Feldnamen (im Symfony-Forms-Format aus Kapitel 15: formular_name[feld_name]) und klickt ihn – SIMULIERT das echte Nutzerverhalten, EINSCHLIESSLICH des in Kapitel 18 gebauten CSRF-Tokens, der AUTOMATISCH im Formular enthalten und mit abgeschickt wird.
Einen eingeloggten Nutzer simulieren
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
public function testGeschuetzteSeiteBrauchtLogin(): void
{
$client = static::createClient();
// Ohne Login: Weiterleitung zur Login-Seite
$client->request('GET', '/projects');
self::assertResponseRedirects('/login');
// Mit simuliertem Login: erfolgreicher Zugriff
$entityManager = static::getContainer()->get(EntityManagerInterface::class);
$user = $entityManager->getRepository(User::class)->findOneBy(['email' => 'anna@example.com']);
$client->loginUser($user);
$client->request('GET', '/projects');
self::assertResponseIsSuccessful();
}loginUser() umgeht den TATSÄCHLICHEN Login-Formular-Prozess und authentifiziert den Test-Client DIREKT – schneller als jedes Mal das Login-Formular auszufüllen, wenn NICHT der Login-Prozess selbst getestet werden soll, sondern eine ANDERE, geschützte Seite.
static::getContainer(): Zugriff auf Services in Tests
getContainer() gibt Zugriff auf DENSELBEN Service Container aus Block 6 – in Tests praktisch, um z. B. direkt Testdaten über den EntityManagerInterface anzulegen, statt den kompletten Registrierungs-Workflow durchzuklicken.
Tipp: Faustregel für Block 7: Unit-Tests (Kapitel 42) für ISOLIERTE Geschäftslogik, funktionale Tests (dieses Kapitel) für KRITISCHE Nutzer-Workflows (Login, Registrierung, Zugriffskontrolle) – ein GESUNDES Projekt hat typischerweise DEUTLICH mehr Unit-Tests als funktionale Tests, da letztere langsamer sind.