IDE
{ }
PHPUnit · Magento 2 · Coverage · PHPStorm
PHPUnit Integrationstests und Coverage
aus PHPStorm für Magento starten

Magento 2 bringt eine eigene Testinfrastruktur mit, die sich erheblich von einem Standard-PHPUnit-Projekt unterscheidet. Wer Integrationstests direkt aus PHPStorm starten, Coverage-Berichte in der IDE sehen und einzelne Testmethoden mit einem Klick debuggen will, braucht spezifische Konfigurationen – die dieser Artikel vollständig abdeckt.

20 Min. Lesezeit PHPUnit · Xdebug Coverage · Run Configurations Magento 2.4.8 · PHP 8.4 · PHPStorm 2024+

1. Magento-Testinfrastruktur verstehen

Magento 2 unterscheidet drei Testarten, die sich in Aufwand, Laufzeit und Infrastrukturanforderungen grundlegend unterscheiden. Unit-Tests testen einzelne Klassen isoliert, ohne Datenbankzugriff und ohne Magento-Bootstrap – sie laufen in Sekunden. Integrationstests benötigen eine vollständig installierte Magento-Instanz mit dedizierter Testdatenbank und können Minuten dauern. Functional-Tests (MFTF) benötigen einen laufenden Browser und einen Webserver – sie gehören in CI-Pipelines, nicht in den IDE-Workflow.

Der Magento-Testrahmen basiert auf PHPUnit, erweitert ihn aber durch eigene Basis-Klassen. Integrationstests erben von Magento\TestFramework\TestCase\AbstractController oder Magento\TestFramework\TestCase\AbstractIntegrationTestCase, nicht von PHPUnit\Framework\TestCase. Diese Basisklassen bootstrappen Magento vor jedem Test, rollback die Datenbank danach zurück und stellen den ObjectManager zur Verfügung. PHPStorm kann all das direkt starten – aber die Run-Konfiguration muss auf die richtige phpunit.xml und den richtigen Working Directory zeigen.

2. Voraussetzungen: Testdatenbank und Umgebungsvariablen

Integrationstests benötigen eine separate Datenbank – sie darf nicht die Produktions- oder Entwicklungsdatenbank sein, da die Tests nach jedem Test-Lauf Daten manipulieren und über Transaktions-Rollbacks bereinigen. Der Datenbankname ist per Konvention magento_integration_tests. In der Datei dev/tests/integration/etc/install-config-mysql.php wird die Datenbankverbindung für Integrationstests konfiguriert. Diese Datei wird nicht in Git versioniert – jeder Entwickler pflegt seine eigene lokale Version.

Im Docker-Setup muss die Testdatenbank im MySQL-Container erstellt werden: docker exec db mysql -uroot -p -e "CREATE DATABASE magento_integration_tests CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;". Anschließend muss die install-config-mysql.php mit den Container-internen Datenbankzugangsdaten befüllt werden. Innerhalb des PHP-Containers ist der MySQL-Host der Service-Name db, nicht localhost. Das ist ein häufiger Fehler: die Host-Adresse aus der normalen .env übernommen, aber im Container gilt eine andere Auflösung als auf dem Host.


<?php
// dev/tests/integration/etc/install-config-mysql.php
// DO NOT COMMIT – contains local database credentials
return [
    'db-host'           => 'db',          // Docker service name, not localhost
    'db-user'           => 'magento',
    'db-password'       => 'magento',
    'db-name'           => 'magento_integration_tests',
    'db-prefix'         => '',
    'backend-frontname' => 'backend',
    'admin-user'        => \Magento\TestFramework\Bootstrap::ADMIN_NAME,
    'admin-password'    => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD,
    'admin-email'       => 'admin@example.com',
    'admin-firstname'   => 'Admin',
    'admin-lastname'    => 'Test',
    'amqp-host'         => '',
    'amqp-port'         => '',
    'elasticsearch-host' => 'opensearch',
    'elasticsearch-port' => '9200',
];

3. phpunit.xml für Unit- und Integrationstests konfigurieren

Magento liefert für Integrationstests eine phpunit.xml.dist unter dev/tests/integration/. Diese Datei sollte kopiert und als phpunit.xml angepasst werden – die Kopie wird ebenfalls nicht versioniert. In der phpunit.xml werden Testsuite-Pfade, Coverage-Filter und Umgebungsvariablen konfiguriert. Der entscheidende Unterschied zur Standard-PHPUnit-Konfiguration: das bootstrap-Attribut zeigt auf dev/tests/integration/framework/bootstrap.php, das den gesamten Magento-Bootstrap-Prozess startet.

Für Unit-Tests gibt es eine separate phpunit.xml.dist unter dev/tests/unit/. Unit-Tests laufen ohne Datenbankzugriff und ohne Bootstrap – sie sind erheblich schneller und eignen sich für schnelle Feedback-Zyklen während der Entwicklung. PHPStorm kann beide Testsuiten als separate Run-Konfigurationen haben: eine für schnelle Unit-Tests beim Speichern, eine für Integrationstests vor dem Commit. Der Filter im Coverage-Report wird über <include>-Pfade in der phpunit.xml gesteuert – nur die eigenen Module sollten dabei gemessen werden, nicht der Magento-Core.

4. Run-Konfigurationen in PHPStorm anlegen

Run-Konfigurationen für PHPUnit werden in PHPStorm unter Run → Edit Configurations → + → PHPUnit angelegt. Für Integrationstests: Test-Scope auf Defined in the configuration file setzen, als Konfigurationsdatei dev/tests/integration/phpunit.xml eintragen, den Remote-Interpreter (Docker Compose phpfpm) auswählen und das Working Directory auf das Magento-Root im Container setzen: /var/www/html. Die Umgebungsvariablen aus der Compose-Datei werden automatisch vererbt.

Für Unit-Tests legt man eine zweite Konfiguration mit dev/tests/unit/phpunit.xml an. Hier kann auch Test-Scope → Directory mit dem Pfad zum eigenen Modul gewählt werden, um nur die Tests des aktuell entwickelten Moduls auszuführen. PHPStorm zeigt alle Run-Konfigurationen in der Toolbar und ermöglicht schnelles Umschalten. Keyboard-Shortcut Ctrl+Shift+F10 startet den Test der aktuell geöffneten Klasse direkt – ohne Run-Konfiguration auswählen zu müssen.


<?php
// dev/tests/integration/phpunit.xml — relevant configuration sections
/*
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="..."
         colors="true"
         columns="80"
         bootstrap="./framework/bootstrap.php"
         stderr="true">

  <testsuites>
    <!-- Custom module tests only — faster than full suite -->
    <testsuite name="Mironsoft_Module Integration Tests">
      <directory>../../../app/code/Mironsoft/*/Test/Integration</directory>
    </testsuite>
  </testsuites>

  <coverage>
    <include>
      <directory suffix=".php">../../../app/code/Mironsoft</directory>
    </include>
    <exclude>
      <directory>../../../app/code/Mironsoft/*/Test</directory>
    </exclude>
  </coverage>

  <php>
    <ini name="memory_limit" value="-1" />
    <ini name="date.timezone" value="Europe/Berlin" />
  </php>
</phpunit>
*/

5. Code-Coverage mit Xdebug aktivieren und auswerten

Code-Coverage für PHPUnit wird in PHPStorm über den Run-With-Coverage-Button ausgelöst – das grüne Play-Symbol mit Schild. PHPStorm aktiviert dabei automatisch Xdebug im Coverage-Modus und sammelt Zeilenabdeckungs-Daten während des Testlaufs. Voraussetzung: Xdebug muss mit xdebug.mode=debug,coverage konfiguriert sein. Der Coverage-Bericht erscheint nach dem Testlauf im Coverage-Panel und zeigt Klassen, Methoden und Zeilen mit farbiger Markierung direkt im Editor.

Grüne Zeilen sind durch mindestens einen Test abgedeckt, rote Zeilen sind ungetestet, gelbe Zeilen sind teilweise abgedeckt (zum Beispiel ein Branch einer if-Abfrage). PHPStorm kann auch HTML-Coverage-Berichte als separate Dateien exportieren: Run → Generate Coverage Report. Diese Berichte eignen sich für CI-Artefakte und Code-Reviews. Wichtig: Coverage-Messung verlangsamt den Testlauf erheblich – für schnelle Feedback-Zyklen Tests ohne Coverage starten, Coverage nur für Analyse-Läufe aktivieren.

6. Einzelne Tests und Methoden direkt aus dem Editor starten

Einer der größten Vorteile der PHPStorm-Integration: PHPUnit-Testmethoden können direkt aus dem Code-Editor gestartet werden. Im Editorfenster erscheint neben jeder Testklasse und Testmethode ein grünes Play-Symbol in der Zeilennummerleiste. Klick darauf startet genau diesen Test mit der letzten aktiven Run-Konfiguration. Rechtsklick bietet zusätzlich Debug an, was Breakpoints in der Testmethode und im getesteten Code aktiviert.

Mit Ctrl+Shift+T springt PHPStorm von einer Klasse zu ihrer Testklasse und zurück. Das ist besonders nützlich beim Test-Driven-Development-Ansatz: Testklasse schreiben, Code schreiben, mit einem Tastendruck zwischen beiden wechseln. PHPStorm erstellt fehlende Testklassen auch automatisch über Navigate → Test und legt dabei die korrekte Verzeichnisstruktur (Test/Unit/, Test/Integration/) automatisch an, wenn das Modul-Namespacing korrekt konfiguriert ist.

7. Eine vollständige Magento-Integrationstest-Klasse schreiben

Magento-Integrationstests unterscheiden sich von Unit-Tests durch den Zugriff auf den ObjectManager, die Datenbank und den gesamten Magento-Kernel. Der ObjectManager-Zugriff funktioniert über Magento\TestFramework\Helper\Bootstrap::getObjectManager(). Das @magentoDbIsolation enabled-Annotation sorgt dafür, dass alle Datenbankoperationen in einer Transaktion laufen und nach dem Test zurückgerollt werden. Mit @magentoDataFixture können Testdaten aus Fixture-Dateien geladen werden.

Ein häufiger Fehler beim Schreiben von Integrationstests: Services mit new Klasse() instanziieren statt über den ObjectManager. Das umgeht die Magento-DI und führt zu Fehlern, weil Abhängigkeiten fehlen. Korrekt ist $this->objectManager->get(ServiceInterface::class) oder $this->objectManager->create(ServiceInterface::class). get gibt die Singleton-Instanz zurück, create erzeugt immer eine neue Instanz – je nach Test-Anforderung die richtige Wahl treffen.


<?php
declare(strict_types=1);

namespace Mironsoft\Catalog\Test\Integration\Model;

use Magento\TestFramework\Helper\Bootstrap;
use Magento\TestFramework\TestCase\AbstractIntegrationTestCase;
use Mironsoft\Catalog\Api\ProductEnricherInterface;
use Mironsoft\Catalog\Model\ProductEnricher;

/**
 * Integration test for ProductEnricher service.
 *
 * @magentoDbIsolation enabled
 * @magentoAppIsolation enabled
 */
class ProductEnricherTest extends AbstractIntegrationTestCase
{
    private ProductEnricherInterface $enricher;

    protected function setUp(): void
    {
        parent::setUp();
        $this->enricher = Bootstrap::getObjectManager()->get(ProductEnricherInterface::class);
    }

    /**
     * @magentoDataFixture Mironsoft_Catalog::Test/Integration/_files/simple_product.php
     */
    public function testEnrichAddsExpectedAttributes(): void
    {
        $productId = 1;
        $result    = $this->enricher->enrich($productId);

        self::assertNotNull($result, 'Enriched product must not be null');
        self::assertTrue($result->hasCustomAttribute('mironsoft_enriched'));
        self::assertEquals('1', $result->getCustomAttribute('mironsoft_enriched')->getValue());
    }

    /**
     * @magentoDataFixture Mironsoft_Catalog::Test/Integration/_files/simple_product.php
     */
    public function testEnrichThrowsForInvalidProduct(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->enricher->enrich(99999);
    }
}

8. Unit- vs. Integrationstests im Vergleich

Die Wahl zwischen Unit- und Integrationstests in Magento ist keine dogmatische Entscheidung, sondern hängt davon ab, was getestet werden soll. Geschäftslogik ohne Datenbankzugriff gehört in Unit-Tests. Repositories, Events, Plugins und Commands, die mit der Magento-Infrastruktur interagieren, gehören in Integrationstests. Beiden Testarten gemeinsam ist der PHPUnit-Rahmen und die PHPStorm-Integration – der Unterschied liegt in Laufzeit, Infrastrukturanforderungen und Testtiefe.

Kriterium Unit-Test Integrationstest Empfehlung
Laufzeit Millisekunden Sekunden bis Minuten Unit-Tests im Entwicklungsflow
Datenbankzugriff Kein (Mocks) Ja, dedizierte DB Repositories → Integrationstest
Magento-Bootstrap Nicht nötig Vollständig DI/Plugin-Verhalten → Integrationstest
Coverage-Tiefe Klassen-intern Module-übergreifend Beide für vollständige Coverage
PHPStorm-Start Sofort aus Editor Run-Config nötig Beide über PHPStorm steuerbar

Die praktische Empfehlung: Unit-Tests decken die Geschäftslogik der Value Objects, Calculator-Klassen und Service-Implementierungen ab. Integrationstests prüfen, ob Plugins korrekt ausgelöst werden, ob Repository-Methoden die richtigen Daten persistieren und ob Events die erwarteten Observer aufrufen. Beide Testarten ergänzen sich – wer nur Unit-Tests hat, testet die Integration nicht; wer nur Integrationstests hat, hat zu langsame Feedback-Zyklen.

9. Typische Fehler und ihre Ursachen

Der häufigste Fehler beim ersten Start von Integrationstests aus PHPStorm: Error: Class not found für Magento-Klassen. Ursache: das Working Directory der Run-Konfiguration zeigt nicht auf das Magento-Root, oder der Autoloader wurde nicht korrekt eingebunden. Lösung: Working Directory in der Run-Konfiguration auf /var/www/html (Container-Pfad) setzen und sicherstellen, dass die phpunit.xml den richtigen Bootstrap-Pfad enthält.

Ein weiterer häufiger Fehler: Datenbankverbindung schlägt fehl. Die Fehlermeldung enthält oft Connection refused auf Host localhost. Ursache: die install-config-mysql.php enthält localhost statt db (Docker-Service-Name). Im Container ist der MySQL-Host der Service-Name aus dem Compose-File, nicht localhost. Nach der Korrektur muss der Integration-Test-Bootstrap neu ausgeführt werden – PHPStorm bietet dafür die Option Invalidate Caches and Restart im File-Menü.

10. Zusammenfassung

PHPUnit-Integrationstests für Magento aus PHPStorm zu starten erfordert vier Schritte: Testdatenbank anlegen und install-config-mysql.php mit Container-internen Zugangsdaten befüllen, phpunit.xml mit eigenem Testsuite-Filter anpassen, Run-Konfigurationen in PHPStorm mit Remote-Interpreter und korrektem Working Directory anlegen, und Xdebug im Coverage-Modus aktivieren. Jeder Schritt ist einmalig pro Projekt – danach laufen Tests mit einem Klick oder Tastendruck direkt aus der IDE.

Der Mehrwert ist dauerhaft: Coverage-Berichte zeigen sofort, welche Zeilen im eigenen Modul ungetestet sind. Einzelne Tests können mit Breakpoints debuggt werden, ohne ein Terminal zu öffnen. Das Wechseln zwischen Implementierung und Testklasse mit Ctrl+Shift+T beschleunigt den TDD-Workflow erheblich. In Magento-Projekten, wo Module komplex miteinander interagieren, ist die IDE-gestützte Testausführung kein Komfort, sondern ein fundamentales Qualitätssicherungswerkzeug.

PHPUnit + Magento in PHPStorm — Das Wichtigste auf einen Blick

Testdatenbank

magento_integration_tests anlegen. install-config-mysql.php mit Docker-Service-Namen db als Host konfigurieren, nicht localhost.

Run-Konfiguration

Remote-Interpreter (phpfpm), Working Directory /var/www/html, Konfigurationsdatei dev/tests/integration/phpunit.xml.

Coverage

xdebug.mode=debug,coverage. Run-With-Coverage-Button aktiviert Coverage automatisch. Bericht erscheint inline im Editor.

Einzeltest

Play-Symbol neben Testmethode startet genau diesen Test. Ctrl+Shift+T wechselt zwischen Klasse und Testklasse.

Mironsoft

Magento-Testing, Code-Qualität und PHPStorm-Integration

Magento-Tests in PHPStorm einrichten?

Wir richten Unit- und Integrationstests für euer Magento-Modul ein – mit Coverage-Berichten, Run-Konfigurationen und vollständiger PHPStorm-Integration.

Test-Setup

Testdatenbank, phpunit.xml und Run-Konfigurationen für euer Magento-Projekt einrichten

Test-Entwicklung

Unit- und Integrationstests für bestehende Module schreiben und Coverage erhöhen

CI-Integration

PHPUnit in GitHub Actions oder GitLab CI mit Coverage-Reporting integrieren

11. FAQ: PHPUnit und Coverage in PHPStorm für Magento

1Integrationstests direkt aus PHPStorm starten?
Ja – mit Remote-Interpreter, Working Directory /var/www/html und korrekter phpunit.xml-Pfadangabe in der Run-Konfiguration.
2Warum separate Testdatenbank?
Integrationstests schreiben und löschen Daten. Ohne separate DB würden Entwicklungsdaten überschrieben. Transaktions-Rollback bereinigt nach jedem Test.
3DbIsolation vs. AppIsolation?
DbIsolation: Transaktion pro Test. AppIsolation: Kernel-Neustart nach Test (langsam). AppIsolation nur bei statischen Zustandsänderungen nötig.
4Code-Coverage aktivieren?
Run-With-Coverage-Button klicken. xdebug.mode=debug,coverage muss gesetzt sein. Bericht erscheint inline im Editor mit grünen/roten Zeilen.
5Einzelne Testmethode starten?
Play-Symbol neben der Methode klicken oder Cursor in die Methode setzen und Ctrl+Shift+F10 drücken.
6Bootstrap schlägt fehl – warum?
Falscher DB-Host (localhost statt db), fehlende install-config-mysql.php, falsches Working Directory oder fehlende Testdatenbank.
7Fixtures in PHP-Dateien?
Ja. @magentoDataFixture mit Pfad zur PHP-Datei unter Test/Integration/_files/. Wird vor dem Test ausgeführt.
8Zwischen Klasse und Testklasse wechseln?
Ctrl+Shift+T öffnet Dialog zum Wechseln oder Erstellen der Testklasse. PHPStorm erkennt Test/Unit/ und Test/Integration/ automatisch.
9Coverage verlangsamt Tests?
Ja, 3–10x langsamer. Für schnelle Zyklen ohne Coverage starten. Coverage nur für Analyse-Läufe und CI aktivieren.
10Coverage auf eigene Module filtern?
In phpunit.xml unter <coverage><include> nur app/code/Mironsoft eintragen. Magento-Core ausschließen – sonst unübersichtlicher Bericht und sehr lange Messung.