Container basierte Test Matrix für mehrere Laufzeitversionen aufbauen
AI generated
FROM
RUN
Docker · Testing · CI/CD
Container basierte Test Matrix
mehrere Laufzeitversionen parallel prüfen

Eine Test Matrix mit Docker prüft eine Anwendung gleichzeitig gegen mehrere PHP- oder Node-Versionen, statt sich auf eine einzige lokale Entwicklerversion zu verlassen. Container machen jede Kombination reproduzierbar, parallel ausführbar und klar getrennt auswertbar, ganz ohne mehrere physische Testmaschinen zu pflegen.

18 Min. Lesezeit GitLab CI Matrix · Compose-Profile · Kompatibilität PHP · Node · Docker

1. Warum eine Test Matrix mehr Sicherheit bringt als ein einzelner Testlauf

Eine Test Matrix prüft eine Anwendung nicht nur einmal, sondern in mehreren Kombinationen relevanter Rahmenbedingungen, meist unterschiedlicher Sprachversionen wie PHP 8.2, 8.3 und 8.4, oder Node 18, 20 und 22. Ohne eine solche Matrix testet ein Team faktisch nur die eine Kombination, die zufällig auf dem eigenen CI-Runner oder der eigenen Entwicklermaschine installiert ist, und erfährt von Inkompatibilitäten mit anderen Versionen erst, wenn ein Kunde oder ein anderes Team sie in Produktion trifft.

Docker macht eine Test Matrix praktikabel, weil jede Version als eigenes, isoliertes Image existiert, ohne dass mehrere Interpreter-Versionen parallel auf demselben Host installiert werden müssten. Statt PHP 8.2, 8.3 und 8.4 nebeneinander auf einer CI-Maschine zu pflegen, mit den bekannten Konflikten zwischen Extensions und Konfigurationsdateien, startet jeder Matrix-Zweig einfach einen anderen Basis-Container mit der jeweils passenden Version.

Der Wert einer gut aufgebauten Test Matrix zeigt sich besonders bei Bibliotheken, Composer-Paketen oder Symfony-Bundles, die von mehreren Projekten mit unterschiedlichen PHP-Versionen genutzt werden. Ohne Matrix-Tests würde eine neue PHP-Version im schlimmsten Fall erst bei einem Kunden auffallen, der als Erster darauf aktualisiert. Mit Matrix-Tests ist die Kompatibilität schon vor dem Release bekannt und dokumentiert.

2. Parametrisierte Dockerfiles für mehrere Laufzeitversionen

Der einfachste Weg zu einer Test Matrix mit Docker ist ein einziges, parametrisiertes Dockerfile, das über ein Build-Argument die Basisversion auswählt. Statt fünf fast identischer Dockerfiles zu pflegen, eines pro PHP-Version, nutzt man ARG PHP_VERSION direkt in der FROM-Zeile und übergibt die konkrete Version beim Build. Das reduziert Wartungsaufwand drastisch, weil Änderungen an der Testumgebung nur an einer Stelle vorgenommen werden müssen.

Wichtig bei dieser Art von Test Matrix ist, dass das Build-Argument ausschließlich die Basisversion beeinflusst, nicht aber Anwendungslogik oder Konfiguration, sonst verletzt man dieselbe Trennung von Build und Konfiguration, die auch bei build once, deploy many gilt. Die Test Matrix testet Kompatibilität mit verschiedenen Laufzeiten, nicht verschiedene Anwendungsvarianten.


# Dockerfile.test -- parametrized for a compatibility test matrix
ARG PHP_VERSION=8.4
FROM php:${PHP_VERSION}-cli AS test

WORKDIR /app
COPY composer.json composer.lock ./
RUN docker-php-ext-install pdo_mysql \
  && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
  && composer install --no-scripts --prefer-dist

COPY . .
CMD ["vendor/bin/phpunit", "--colors=never"]

3. Matrix-Jobs in GitLab CI definieren

GitLab CI unterstützt native Matrix-Jobs über den parallel: matrix-Schlüssel, mit dem eine Test Matrix deklarativ statt imperativ beschrieben wird. Ein einzelner Job wird für jede Kombination der angegebenen Variablen automatisch dupliziert, jeweils mit eigenem, eindeutigem Job-Namen in der Pipeline-Übersicht. Das erspart, für jede PHP-Version einen eigenen, fast identischen Job manuell zu definieren und bei Änderungen mehrfach pflegen zu müssen.

Bei größeren Test Matrix Konfigurationen mit mehreren Dimensionen, etwa PHP-Version kombiniert mit Datenbank-Version, wächst die Anzahl der Kombinationen schnell. GitLab erlaubt es, mehrere Variablenlisten im selben matrix-Block zu kombinieren, was ein vollständiges kartesisches Produkt erzeugt. Es lohnt sich, bewusst zu entscheiden, welche Kombinationen tatsächlich getestet werden müssen, statt jede theoretisch mögliche Kombination laufen zu lassen.


# .gitlab-ci.yml -- test matrix across PHP versions and database versions
test-matrix:
  stage: test
  parallel:
    matrix:
      - PHP_VERSION: ["8.2", "8.3", "8.4"]
        DB_VERSION: ["8.0", "8.4"]
  image: docker:24
  services:
    - docker:24-dind
  script:
    - docker build --build-arg PHP_VERSION=$PHP_VERSION -f Dockerfile.test -t test-$PHP_VERSION .
    - docker run --rm test-$PHP_VERSION
  allow_failure:
    exit_codes: 2  # deprecation-only failures don't block the pipeline

4. Docker Compose Profile für lokale Matrix-Läufe

Nicht jede Test Matrix muss ausschließlich in CI laufen. Für lokale Entwicklung ist es hilfreich, dieselbe Matrix über Docker Compose Profile abzubilden, sodass ein Entwickler gezielt gegen eine einzelne oder mehrere Versionen testen kann, ohne die komplette CI-Pipeline anzustoßen. Jedes Profil repräsentiert dabei eine Version, und docker compose --profile php83 up startet nur den entsprechenden Service.

Dieser Ansatz beschleunigt die Test Matrix Entwicklung erheblich, weil ein Entwickler eine vermutete Inkompatibilität sofort lokal gegen die betroffene Version nachvollziehen kann, statt auf einen vollständigen CI-Lauf zu warten. Compose-Profile und GitLab-Matrix-Jobs sollten dieselbe Versionsliste referenzieren, idealerweise aus einer gemeinsamen Variablen-Datei, damit beide Umgebungen niemals auseinanderlaufen.

5. Parallelisierung und Laufzeit unter Kontrolle halten

Eine Test Matrix mit vielen Kombinationen kann die Gesamtlaufzeit der Pipeline erheblich verlängern, wenn alle Matrix-Zweige seriell statt parallel ausgeführt werden. GitLab CI führt Matrix-Jobs standardmäßig parallel auf verfügbaren Runnern aus, was die Wandzeit gegenüber einer seriellen Ausführung drastisch reduziert, allerdings auf Kosten von mehr gleichzeitig benötigten Runner-Kapazitäten.

Für Teams mit begrenzter Runner-Kapazität lohnt es sich, die Test Matrix nach Kritikalität zu staffeln: Die aktuell unterstützte Version läuft bei jedem Commit, ältere oder zukünftige Versionen nur nächtlich oder bei Tags. Das rules-Konstrukt in GitLab CI erlaubt genau diese Staffelung, indem einzelne Matrix-Kombinationen an unterschiedliche Trigger-Bedingungen gebunden werden.


# .gitlab-ci.yml -- staggered matrix: current version always, others nightly
test-matrix-staggered:
  stage: test
  parallel:
    matrix:
      - PHP_VERSION: "8.4"
        WHEN: "always"
      - PHP_VERSION: ["8.2", "8.3"]
        WHEN: "nightly"
  rules:
    - if: '$WHEN == "always"'
    - if: '$WHEN == "nightly" && $CI_PIPELINE_SOURCE == "schedule"'
  script:
    - docker build --build-arg PHP_VERSION=$PHP_VERSION -f Dockerfile.test -t test-$PHP_VERSION .
    - docker run --rm test-$PHP_VERSION

6. Ergebnisse konsolidieren: ein Kompatibilitätsbericht pro Version

Verstreute grüne und rote Job-Symbole in der Pipeline-Übersicht sind für eine Test Matrix mit vielen Kombinationen unübersichtlich. Sinnvoller ist ein konsolidierter Kompatibilitätsbericht, der pro Version zusammenfasst, wie viele Tests bestanden, fehlgeschlagen oder übersprungen wurden. JUnit-XML-Reports aus jedem Matrix-Zweig lassen sich in GitLab über artifacts.reports.junit einsammeln und in der Merge-Request-Ansicht gemeinsam darstellen.

Für ein Projekt, das seine Test Matrix öffentlich dokumentieren möchte, etwa eine Composer-Bibliothek, bietet sich zusätzlich eine automatisch generierte Kompatibilitätstabelle im README an, die bei jedem erfolgreichen Matrix-Lauf aktualisiert wird. Nutzer sehen dann auf einen Blick, welche PHP-Versionen offiziell unterstützt und getestet werden, statt sich auf eine veraltete, manuell gepflegte Liste verlassen zu müssen.

7. Build-Caching in der Matrix ohne redundante Downloads

Ohne Caching lädt jeder Zweig einer Test Matrix dieselben Composer- oder npm-Abhängigkeiten erneut herunter, was bei fünf oder mehr Versionen die Gesamtlaufzeit unnötig aufbläht. BuildKit-Cache-Mounts (RUN --mount=type=cache) helfen hier nur bedingt, weil unterschiedliche PHP-Versionen teils unterschiedliche Abhängigkeitsauflösungen erfordern. Ein gemeinsamer Registry-basierter Cache für Composer, der über COMPOSER_CACHE_DIR konfiguriert und als Docker Volume zwischen Matrix-Läufen geteilt wird, reduziert die redundanten Downloads spürbar.

Bei einer Test Matrix mit GitLab CI lässt sich der Cache-Schlüssel gezielt um die PHP-Version erweitern, sodass jede Version ihren eigenen Cache-Bereich bekommt, ohne dass sich unterschiedliche Dependency-Auflösungen gegenseitig überschreiben. cache: key: "composer-$PHP_VERSION" ist dafür die einfachste, robusteste Lösung.

8. Typische Fehler beim Aufbau einer Test Matrix

Der häufigste Fehler bei einer Test Matrix ist, zu viele Kombinationen zu testen, ohne den tatsächlichen Nutzen zu hinterfragen. Ein Projekt, das PHP 7.4 gar nicht mehr unterstützt, sollte diese Version auch nicht mehr in der Matrix führen, nur weil sie historisch einmal dort stand. Jede zusätzliche Kombination kostet Laufzeit und Runner-Minuten, die an anderer Stelle fehlen.


# WRONG: testing an unsupported version out of habit
test-matrix:
  parallel:
    matrix:
      - PHP_VERSION: ["7.4", "8.0", "8.1", "8.2", "8.3", "8.4"]
      # 7.4 and 8.0 are long past end of life -- wasted CI minutes

# RIGHT: matrix limited to officially supported versions
test-matrix:
  parallel:
    matrix:
      - PHP_VERSION: ["8.2", "8.3", "8.4"]
      # matches composer.json's "require": {"php": "^8.2"}

Ein zweiter Fehler ist, Matrix-Ergebnisse zu ignorieren, wenn nur eine ältere Version fehlschlägt. Wenn ein Team gewohnheitsmäßig einen fehlgeschlagenen Matrix-Zweig als bekanntes, unwichtiges Problem abtut, verliert die gesamte Test Matrix ihren Zweck. Entweder die Version wird offiziell unterstützt und ein Fehlschlag blockiert den Merge, oder die Version wird aus der Matrix entfernt, ein dritter, ignorierter Zustand untergräbt das Vertrauen in alle Matrix-Ergebnisse.

9. Test-Matrix-Strategien im direkten Vergleich

Die folgende Tabelle vergleicht einen einzelnen Testlauf mit einer vollständigen Test Matrix über mehrere Laufzeitversionen.

Aspekt Einzelner Testlauf Container basierte Test Matrix Konsequenz
Getestete Versionen Nur eine, zufällig installierte Alle offiziell unterstützten Kompatibilitätslücken früh sichtbar
Wartung mehrerer Interpreter Konflikte auf demselben Host Isoliert je Container Keine Extension-Konflikte
Ausführung Seriell, manuell wiederholt Parallel über Matrix-Jobs Kürzere Gesamtlaufzeit
Ergebnisdarstellung Ein einzelnes Ergebnis Konsolidierter Bericht pro Version Klare Kompatibilitätsaussage
Wartungsaufwand Gering, aber blind Höher, aber informiert Bewusste statt zufällige Abdeckung

Der Mehraufwand einer Test Matrix lohnt sich besonders für Projekte mit externen Nutzern, die selbst über die unterstützte Versionsbandbreite entscheiden. Interne Projekte mit einer einzigen, festen Produktionsversion profitieren weniger stark, sollten aber zumindest die aktuelle und die nächste kommende Version testen, um Migrationen frühzeitig vorzubereiten.

Mironsoft

Container-Testing, CI-Pipelines und Kompatibilitätsprüfungen

Test Matrix für mehrere Laufzeitversionen aufbauen?

Wir richten container basierte Test Matrizen ein, die eure Anwendung parallel gegen alle relevanten PHP- und Node-Versionen prüfen, mit konsolidierten Berichten statt verstreuten Job-Symbolen.

Matrix-Design

Sinnvolle Versionsabdeckung ohne unnötige Kombinationen festlegen

CI-Integration

GitLab-Matrix-Jobs und Compose-Profile für lokale Läufe einrichten

Reporting

Konsolidierte Kompatibilitätsberichte in Merge Requests einbinden

10. Zusammenfassung

Eine Test Matrix auf Container-Basis löst ein Problem, das viele Teams stillschweigend hinnehmen: Anwendungen werden faktisch nur gegen eine einzige, zufällig installierte Laufzeitversion geprüft. Docker macht es praktikabel, mehrere PHP- oder Node-Versionen parallel und isoliert zu testen, ohne Konflikte zwischen Interpreter-Installationen auf demselben Host. GitLab-Matrix-Jobs und Compose-Profile bilden dieselbe Versionsliste sowohl in CI als auch lokal ab.

Der entscheidende Faktor für den langfristigen Nutzen einer Test Matrix ist Disziplin: Nur offiziell unterstützte Versionen gehören in die Matrix, Fehlschläge müssen konsequent behandelt werden, und konsolidierte Berichte ersetzen verstreute Job-Symbole. Wer diese Punkte beachtet, gewinnt eine verlässliche, automatisierte Aussage über Kompatibilität, statt sich auf Zufallsfunde durch Kunden oder andere Teams zu verlassen.

Container basierte Test Matrix — Das Wichtigste auf einen Blick

Parametrisiertes Dockerfile

ARG PHP_VERSION in der FROM-Zeile erspart mehrere fast identische Dockerfiles.

GitLab Matrix-Jobs

parallel: matrix dupliziert einen Job automatisch für jede Versionskombination.

Konsolidierte Berichte

JUnit-Reports pro Matrix-Zweig sammeln, statt verstreute Job-Symbole einzeln zu prüfen.

Bewusste Abdeckung

Nur offiziell unterstützte Versionen testen, keine historischen Altlasten mitschleppen.

11. FAQ: Container basierte Test Matrix

1Was ist eine Test Matrix mit Docker?
Ein Testaufbau, der mehrere Laufzeitversionen parallel in isolierten Containern prüft.
2Warum reicht ein einzelner Testlauf nicht?
Er prüft nur eine zufällige Version, Inkompatibilitäten bleiben sonst unentdeckt.
3Wie wird ein Dockerfile parametrisiert?
Über ARG PHP_VERSION direkt in der FROM-Zeile, eine Datei für alle Versionen.
4Wie definiert man Matrix-Jobs in GitLab CI?
Über parallel: matrix, GitLab dupliziert den Job automatisch pro Kombination.
5Wie testet man lokal dieselbe Matrix?
Über Docker Compose Profile mit derselben Versionsliste wie in CI.
6Wie bleibt die Laufzeit kontrollierbar?
Durch parallele Ausführung und Staffelung nach Kritikalität der Versionen.
7Wie werden Ergebnisse konsolidiert?
Über JUnit-Reports pro Zweig, gesammelt via artifacts.reports.junit.
8Wie funktioniert Caching in der Matrix?
Über versionsabhängige Cache-Schlüssel wie composer-$PHP_VERSION.
9Sollten End-of-Life-Versionen bleiben?
Nein, nur offiziell unterstützte Versionen gehören in die Matrix.
10Was tun bei fehlschlagender älterer Version?
Entweder blockiert es den Merge, oder die Version wird aus der Matrix entfernt.