@test
assert
PHPUnit · Docker · PhpStorm · Xdebug
PHPStorm und Docker:
PHPUnit im Container sauber ausführen

Wer PHP in Docker-Containern entwickelt und trotzdem Tests direkt aus der IDE heraus starten will, steht vor einer Konfigurationsaufgabe: PhpStorm muss wissen, welcher PHP-Interpreter im Container steckt, wie Xdebug erreichbar ist und wie Pfade zwischen Host und Container gemappt werden. Dieser Artikel zeigt den vollständigen Weg.

12 Min. Lesezeit Remote-Interpreter · Run Configurations · Coverage · Xdebug PHPUnit 11 · PHP 8.4 · Docker Compose

1. Warum PHPUnit nicht lokal ausführen?

Die naheliegende Lösung für das Ausführen von PHPUnit-Tests klingt einfach: PHP lokal installieren, vendor/bin/phpunit aufrufen, fertig. In der Praxis erzeugt dieser Weg jedoch einen klassischen Environments-Drift. Die lokale PHP-Version weicht von der im Container ab, Datenbankverbindungen funktionieren nur im Container-Netzwerk, und Umgebungsvariablen wie APP_ENV, DB_HOST oder API-Schlüssel sind im Container anders gesetzt als auf dem Entwickler-Laptop. Tests, die lokal grün sind, schlagen in der CI-Pipeline fehl – oder umgekehrt.

Die saubere Lösung ist der Remote-Interpreter in PhpStorm: PhpStorm kommuniziert über Docker oder SSH direkt mit dem PHP-Prozess im Container. Alle Tests laufen in exakt derselben Umgebung wie in der Produktion – mit denselben PHP-Erweiterungen, derselben php.ini und denselben Umgebungsvariablen. Der Entwickler sieht das Ergebnis dennoch direkt in der IDE, inklusive Fehlernavigation, Testbaum und Coverage-Highlighting.

Für Projekte mit mehreren Entwicklern ist das besonders wichtig: Eine geteilte docker-compose.yml und eine committete .idea/-Konfiguration sorgen dafür, dass alle dasselbe Setup verwenden. Kein "funktioniert bei mir"-Szenario mehr, weil der Kollege PHP 8.2 lokal hat und der Container PHP 8.4 verwendet.

2. Voraussetzungen: Docker-Compose und PHP-Container

Der erste Schritt ist ein sauber aufgesetzter PHP-Container mit Xdebug und den für das Projekt notwendigen PHP-Erweiterungen. Das Dockerfile sollte xdebug als PECL-Extension installieren und über die php.ini oder eine separate xdebug.ini konfigurieren. Wichtig: Xdebug 3 verwendet andere INI-Schlüssel als Xdebug 2 – xdebug.mode ersetzt das frühere xdebug.remote_enable. Für PHPUnit-Coverage setzt man xdebug.mode=coverage, für Debugging xdebug.mode=debug,develop.

Die Docker-Compose-Datei muss den PHP-Container so konfigurieren, dass PhpStorm ihn erreichen kann. Bei Docker Desktop auf macOS ist der Host über host.docker.internal erreichbar; auf Linux muss man entweder das Host-Netzwerk verwenden oder die IP explizit setzen. Die Variable XDEBUG_CONFIG="client_host=host.docker.internal" in der Compose-Datei stellt sicher, dass Xdebug die Verbindung zur IDE aufbauen kann.


# docker-compose.yml — PHP service with Xdebug for PHPUnit
services:
  php:
    build:
      context: .
      dockerfile: .docker/php/Dockerfile
    volumes:
      - .:/var/www/html:cached
      - .docker/php/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
    environment:
      XDEBUG_MODE: "coverage,debug"
      XDEBUG_CONFIG: "client_host=host.docker.internal client_port=9003 start_with_request=trigger"
      PHP_IDE_CONFIG: "serverName=mironsoft-docker"
    networks:
      - app-network

# .docker/php/xdebug.ini
# xdebug.mode=coverage,debug
# xdebug.client_host=host.docker.internal
# xdebug.client_port=9003
# xdebug.start_with_request=trigger
# xdebug.log_level=0

3. Remote-Interpreter in PhpStorm einrichten

Der Remote-Interpreter wird in PhpStorm unter Settings → PHP → CLI Interpreter eingerichtet. Dort wählt man "From Docker, Vagrant, VM, WSL, Remote" und wählt "Docker Compose" aus. PhpStorm fragt nach der docker-compose.yml-Datei und dem Service-Namen – in unserem Fall php. Nach einem Klick auf "OK" erkennt PhpStorm automatisch die PHP-Version und die installierten Erweiterungen aus dem Container.

Wichtig: Der PHP-Executable-Pfad muss im Container korrekt sein. Bei offiziellen PHP-Images liegt das Binary unter /usr/local/bin/php. PhpStorm zeigt nach der Konfiguration die erkannte PHP-Version und die geladenen Erweiterungen an – hier prüft man, ob xdebug und alle für das Projekt benötigten Erweiterungen erscheinen. Wenn eine Erweiterung fehlt, fehlt sie im Container und muss im Dockerfile nachinstalliert werden.

Für Mark-Shust-Docker-Setups (das Setup, das wir bei Mironsoft verwenden) ist der Service-Name typischerweise phpfpm und der Interpreter liegt unter /usr/local/bin/php. PhpStorm speichert den Interpreter in der Projektdatei unter .idea/php.xml – diese Datei sollte ins Repository committed werden, damit alle Teammitglieder dieselbe Konfiguration erhalten.

4. PHPUnit-Konfiguration im Projekt

Die phpunit.xml (oder phpunit.xml.dist) im Projektstamm steuert, wie PHPUnit Tests findet, welche Bootstrap-Datei geladen wird und welche Coverage-Berichte erzeugt werden. Eine saubere Konfiguration definiert explizit das Bootstrap-File, die Test-Suites und die Source-Paths für Coverage. In Magento-Projekten ist das Bootstrap typischerweise ein Autoloader aus dev/tests/unit/framework/bootstrap.php.

Für Projekte außerhalb von Magento reicht meist der Composer-Autoloader als Bootstrap: <bootstrap>vendor/autoload.php</bootstrap>. Die <source>-Direktive in PHPUnit 11 ersetzt das frühere <whitelist>: Sie gibt an, welche Verzeichnisse bei der Coverage-Analyse berücksichtigt werden. Nur Code, der hier aufgelistet ist, erscheint in den Coverage-Reports – undefinierter Code aus Vendor-Paketen bleibt außen vor.


<?xml version="1.0" encoding="UTF-8"?>
<!-- phpunit.xml.dist — Project root, committed to repository -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         cacheDirectory=".phpunit.cache"
         executionOrder="depends,defects"
         requireCoverageMetadata="false"
         beStrictAboutCoverageMetadata="false">

  <testsuites>
    <testsuite name="Unit">
      <directory>tests/Unit</directory>
    </testsuite>
    <testsuite name="Integration">
      <directory>tests/Integration</directory>
    </testsuite>
  </testsuites>

  <source>
    <include>
      <directory suffix=".php">src</directory>
    </include>
    <exclude>
      <directory>src/Migrations</directory>
    </exclude>
  </source>

  <php>
    <env name="APP_ENV" value="testing"/>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
  </php>
</phpunit>

5. Run Configuration in PhpStorm anlegen

Mit dem eingerichteten Remote-Interpreter legt man nun unter Run → Edit Configurations → + → PHPUnit eine neue Run Configuration an. Als Interpreter wählt man den eben eingerichteten Docker-Interpreter. Als "Test scope" kann man die gesamte Test-Suite (phpunit.xml), ein einzelnes Verzeichnis oder eine einzelne Klasse auswählen. PhpStorm liest die phpunit.xml automatisch aus dem Projekt und bietet die konfigurierten Test-Suites zur Auswahl an.

Die Run Configuration kann direkt über die grüne Pfeil-Schaltfläche in der Toolbar oder mit Ctrl+R (macOS: Cmd+R) gestartet werden. PhpStorm startet dann einen Container (oder verbindet sich mit dem laufenden) und führt PHPUnit darin aus. Das Ergebnis erscheint im "Run"-Fenster mit einem Testbaum – grüne Tests, rote Fehler mit Stack-Traces und die Möglichkeit, direkt zur fehlgeschlagenen Zeile zu springen.

Für häufig verwendete Setups empfiehlt sich, die Run Configuration zu teilen: Run → Edit Configurations → Checkmark "Share through VCS". PhpStorm speichert die Konfiguration dann unter .idea/runConfigurations/PHPUnit.xml und alle Teammitglieder können sie sofort verwenden, ohne die Konfiguration manuell nachzubauen.

6. Xdebug im Container für PHPUnit aktivieren

Xdebug und PHPUnit-Coverage im Container erfordern ein sorgfältiges Zusammenspiel. Xdebug 3 kennt verschiedene Modi: debug für das Setzen von Breakpoints und schrittweises Debuggen, coverage für die Coverage-Analyse und develop für erweiterte Fehlerausgaben. Für PHPUnit-Coverage reicht xdebug.mode=coverage. Wer gleichzeitig debuggen will, setzt xdebug.mode=debug,coverage.

Ein häufiges Problem: Xdebug verlangsamt PHPUnit erheblich. Eine Test-Suite, die ohne Xdebug in 2 Sekunden durchläuft, kann mit aktivem Xdebug 15 bis 30 Sekunden benötigen. Die Lösung: Xdebug nur bei Coverage-Runs aktivieren. Das lässt sich über eine separate Docker-Compose-Override-Datei oder über die XDEBUG_MODE-Umgebungsvariable steuern. Für normale Test-Runs ohne Coverage setzt man XDEBUG_MODE=off – so läuft PHPUnit mit voller Geschwindigkeit.

7. Code-Coverage direkt in PhpStorm anzeigen

Code-Coverage in PhpStorm ist eine der überzeugendsten Funktionen des Remote-Interpreter-Setups. Wenn man die Run Configuration mit dem Coverage-Button startet (Schild-Symbol neben dem Play-Button), führt PhpStorm PHPUnit mit --coverage-xml aus und liest das Ergebnis ein. Danach werden direkt im Editor die abgedeckten Zeilen grün und nicht abgedeckte rot markiert – ohne dass man einen Browser oder ein separates Tool öffnen muss.

Im "Coverage"-Fenster zeigt PhpStorm eine Baumstruktur aller Klassen mit ihren Line- und Branch-Coverage-Prozentzahlen. Klassen mit unter 80 % Line-Coverage fallen sofort auf. Man kann direkt in die Klasse springen und sieht, welche Zeilen nie von einem Test erreicht wurden. Diese Integration ist nur möglich, wenn der Remote-Interpreter korrekt konfiguriert ist und Xdebug im Container den Mode coverage aktiviert hat.

8. Pfad-Mapping: Host vs. Container korrekt konfigurieren

Das häufigste Problem bei Remote-Interpreter-Setups ist das Pfad-Mapping. PhpStorm läuft auf dem Host, der Code liegt unter /home/user/project. Im Container liegt derselbe Code unter /var/www/html. Ohne korrektes Mapping kann PhpStorm Stack-Traces und Coverage-Daten nicht korrekt auf Hostpfade zurückrechnen – Fehlernavigation und Coverage-Highlighting funktionieren dann nicht.

Das Mapping konfiguriert man unter Settings → PHP → Servers. Dort legt man einen Server mit dem Namen an, der in PHP_IDE_CONFIG="serverName=mironsoft-docker" gesetzt ist. Dann definiert man das Path-Mapping: Hostpfad links (/home/mir/development/mironsoft/src), Container-Pfad rechts (/var/www/html). Mit korrektem Mapping springt PhpStorm bei einem fehlgeschlagenen Test direkt zur richtigen Zeile im Host-Filesystem – auch wenn der Fehler technisch im Container aufgetreten ist.

Aspekt Lokaler PHP-Interpreter Docker Remote-Interpreter Empfehlung
Umgebungsparität Lokal ≠ CI/Produktion Identisch mit CI und Produktion Docker Remote
Startgeschwindigkeit Sofort, kein Container-Start Container muss laufen Lokal (nur für Schnelltests)
Datenbankzugriff Nur mit lokalem DB-Server Container-Netzwerk, kein Setup Docker Remote
Xdebug-Breakpoints Direkt, kein Mapping Mit Path-Mapping möglich Docker mit korrektem Mapping
Team-Konsistenz Jeder hat andere PHP-Version Alle nutzen denselben Container Docker Remote

9. Lokaler PHP vs. Docker-Interpreter im Vergleich

Der lokale PHP-Interpreter hat einen entscheidenden Vorteil: Er startet sofort. Wenn der Container noch nicht läuft, muss PhpStorm erst auf das Hochfahren warten – je nach Image mehrere Sekunden. Für Teams, die den Container ohnehin dauerhaft laufen haben, spielt das keine Rolle. Für Entwickler, die den Container nur bei Bedarf starten, kann es den Workflow stören.

Der Docker Remote-Interpreter gewinnt dagegen in allen Aspekten, die für die Testqualität relevant sind: Umgebungsparität, Datenbankzugriff und Team-Konsistenz. Ein Test, der lokal mit SQLite läuft, aber im Container mit MySQL, kann Fehler verbergen, die erst in der CI auffallen. Das Pfad-Mapping ist nach einmaliger Einrichtung kein Problem mehr – PhpStorm speichert es in der Projektkonfiguration.

Mironsoft

PHPUnit-Setup, Docker-Integration und CI/CD für PHP-Projekte

PHPUnit im Container – sauber konfiguriert und CI-ready?

Wir richten PHPUnit für euer Docker-Projekt ein: Remote-Interpreter, Xdebug-Coverage, Run Configurations und CI-Integration – damit Tests lokal und in der Pipeline identisch laufen.

Docker-Setup

PHP-Container mit Xdebug 3, korrekte INI-Konfiguration und Compose-Integration

PhpStorm-Konfiguration

Remote-Interpreter, Path-Mapping, Run Configurations und Coverage-Anzeige

CI-Integration

Dieselben Tests in GitLab CI und GitHub Actions ausführen – ohne Anpassungen

10. Zusammenfassung

PHPUnit im Docker-Container mit PhpStorm auszuführen erfordert einmalig sorgfältige Konfiguration, zahlt sich aber sofort aus. Der Remote-Interpreter stellt sicher, dass Tests in exakt derselben Umgebung laufen wie in CI und Produktion. Xdebug 3 mit xdebug.mode=coverage liefert Coverage-Daten, die PhpStorm direkt im Editor visualisiert. Das Pfad-Mapping zwischen Host und Container macht Fehlernavigation und Stack-Trace-Links funktionsfähig.

Die wichtigsten Schritte: PHP-Container mit Xdebug im Dockerfile aufsetzen, Remote-Interpreter in PhpStorm unter Settings → PHP konfigurieren, Server mit Path-Mapping anlegen, phpunit.xml mit korrekten Source-Pfaden versehen und eine Run Configuration erstellen. Nach dieser einmaligen Einrichtung startet man PHPUnit mit einem Klick direkt aus PhpStorm – und erhält das Ergebnis mit Testbaum, Coverage-Highlighting und direkter Fehlernavigation in den Container-Code.

PHPUnit im Docker-Container — Das Wichtigste auf einen Blick

Remote-Interpreter

Settings → PHP → CLI Interpreter → Docker Compose. Service-Name und PHP-Binary korrekt angeben. Wird in .idea/php.xml gespeichert.

Xdebug-Modus

xdebug.mode=coverage für Coverage-Reports. XDEBUG_MODE=off für schnelle Test-Runs ohne Coverage-Overhead.

Pfad-Mapping

Settings → PHP → Servers: Hostname-Mapping mit serverName aus PHP_IDE_CONFIG. Host-Pfad ↔ Container-Pfad korrekt eintragen.

phpunit.xml

<source> für Coverage-Pfade, Bootstrap-Datei, Test-Suites und Umgebungsvariablen definieren. Ins Repository committen.

11. FAQ: PHPUnit im Docker-Container mit PhpStorm

1Wie richte ich einen Docker Remote-Interpreter in PhpStorm ein?
Settings → PHP → CLI Interpreter → + → Docker Compose. docker-compose.yml und Service-Name angeben. PhpStorm erkennt PHP-Version und Extensions automatisch.
2Warum schlägt PHPUnit im Container fehl, läuft aber lokal?
Fehlende Extensions im Container, andere PHP-Version, Umgebungsvariablen nicht gesetzt oder falsche Pfade in phpunit.xml. Xdebug-Modus und INI prüfen.
3Welcher Xdebug-Modus ist für PHPUnit-Coverage richtig?
xdebug.mode=coverage. Für normale Runs ohne Coverage: XDEBUG_MODE=off – das verhindert den Performance-Einbruch durch Xdebug.
4Was ist PHP_IDE_CONFIG und wozu brauche ich es?
Teilt Xdebug mit, welche Server-Konfiguration aus PhpStorm verwendet wird. PhpStorm sucht den Eintrag in Settings → PHP → Servers für das Path-Mapping.
5Wie konfiguriere ich das Pfad-Mapping?
Settings → PHP → Servers → +. Server-Name muss mit PHP_IDE_CONFIG übereinstimmen. Host-Pfad links, Container-Pfad rechts eintragen.
6Kann ich die PhpStorm-Konfiguration mit dem Team teilen?
.idea/php.xml und .idea/runConfigurations/ ins Repository committen. Bei Run Configurations "Share through VCS" aktivieren.
7Wie verhindere ich, dass Xdebug alle Tests verlangsamt?
XDEBUG_MODE=off als Standard. Nur für Coverage-Runs auf coverage umschalten. In PhpStorm zwischen "Run" und "Run with Coverage" wählen.
8Wie führe ich einen einzelnen Test aus dem Editor heraus aus?
Play-Button neben der @test-Annotation klicken oder Cursor in die Test-Methode setzen und Ctrl+Shift+R (Cmd+Shift+R auf macOS) drücken.
9Muss der Docker-Container laufen, bevor ich PHPUnit starte?
Ja. Empfehlung: docker compose up -d dauerhaft im Hintergrund laufen lassen. PhpStorm kann den Container bei Bedarf starten, aber das verlängert den Test-Start.
10Warum zeigt PhpStorm keine Coverage-Markierungen?
Xdebug nicht im coverage-Modus, Pfad-Mapping stimmt nicht oder <source> in phpunit.xml deckt die Datei nicht ab. Alle drei Punkte der Reihe nach prüfen.