thePHP.cc Logo English Kontakt
Migration auf PHPUnit 9

Migration auf PHPUnit 9

Im Spannungsfeld zwischen Fortschritt und Rückwärtskompatibilität darf eine gute Developer Experience nicht zu kurz kommen.

Februar: die Zeit im Jahr, in der ich Artikel wie "Hilfe! Meine Tests funktionieren nicht mehr." oder " The Death Star Version Constraint " schreibe. Dieses Mal schreibe ich über die Migration von PHPUnit 8 auf PHPUnit 9 .

Keine Angst vor Major-Versionen

Die deutsche Sprache erlaubt durch die Verkettung von Substantiven die Bildung von praktisch endlos langen Worten. Worte wie beispielsweise „Hauptversionsnummerehöhungsangst“. Mit diesem Wort, oder vielleicht eher Unwort, beschreibt man die Angst vieler Softwareprojekte vor dem Erhöhen der Hauptversionsnummer (Englisch: Major Version), beispielsweise von 8.5 auf 9.0. So lagen beispielsweise elf Jahre zwischen der Veröffentlichung von PHP 5.0 und der Veröffentlichung von PHP 7.0. Dass es auch anders geht, hat Mozilla mit dem Release-Prozess für den Firefox-Browser gezeigt, von dem alle sechs bis acht Wochen eine neue Major-Version erscheint. Ab diesem Jahr will Mozilla sogar alle vier Wochen eine neue Major-Version von Firefox veröffentlichen. Vor diesem Hintergrund ist die jährliche Erhöhung der Hauptversionsnummber von PHPUnit geradezu zahm.

Am 7. Februar 2020 ist mit PHPUnit 9 eine neue Major-Version des Standard-Testframeworks der PHP-Welt erschienen. Mit einer neuen Hauptversionsnummer wie „9“ verbindet man üblicherweise eine Flut von neuen Features oder andere Leistungsverbesserungen. Das PHPUnit-Projekt handhabt dies jedoch anders und so bringt beispielsweise PHPUnit 9.0 keine signifikanten neuen Features. Diese werden erst im Laufe des Jahres mit PHPUnit 9.1 et cetera kommen.

Alle zwei Monate, und zwar am ersten Freitag im Februar, April, Juni, August, Oktober und Dezember, erscheint eine neue Version von PHPUnit. Bei der neuen Version im Februar handelt es sich um eine Major-Version, bei den anderen Versionen um Minor-Versionen. Den Grundsätzen von Semantic Versioning folgend bedeutet dies, dass die Februar-Version nicht abwärts kompatibel mit den Vorgängerversionen ist und beispielsweise Features entfernt. Die anderen Versionen hingegen sind zu ihren Vorgängern abwärtskompatibel und führen beispielsweise neue Features ein. Aber warum werden jedes Jahr im Februar Features aus PHPUnit entfernt oder so geändert, dass ein Anpassen der eigenen Tests notwendig wird?

Warum PHPUnit Funktionalität entfernt

PHPUnit ist fast zwanzig Jahre alt. Das bedeutet zum einen, dass Features, die vor fünf, zehn, fünfzehn oder gar zwanzig Jahren noch notwendig waren, heute keinen Nutzen mehr haben. In vielen Fällen, beispielsweise die Unterstützung des direkten Testens von privaten Objekt-Eigenschaften, hat alte Funktionalität nicht nur keinen positiven Nutzen mehr, sondern vielmehr sogar einen negativen Effekt: Entwickler denken, nur weil es eine Funktionalität in PHPUnit gibt, ist es eine gute Idee, diese zu verwenden. Dies kann im schlimmsten Fall dazu führen, dass man Software unsauber entwickelt. Zum anderen muss eine Software wie PHPUnit ständig an Änderungen im Ökosystem angepasst werden: neue Versionen von PHP sind ebenso zu unterstützen wie neue Distributionskanäle. Die Migration einer Codebasis wie der von PHPUnit ist eben nicht über Nacht von PHP 5 nach PHP 7 zu migrieren. Und auch die Ablösung von PEAR-Paketen durch die Unterstützung für Composer PHP-Archive (PHAR) hat damals einiges an Zeit gekostet. Darüber hinaus steigt die Komplexität des Codes durch Workarounds für Bugs in bestimmten PHP-Versionen und Unterstützung unterschiedlicher PHP-Versionen in derselben PHPUnit-Version ebenso wie durch das Hinzufügen neuer Funktionalität.

Lange Zeit war ich, mehr oder weniger, der einzige Entwickler von PHPUnit. Weil eben nur ich bereit war, mit dieser Codebasis zu arbeiten. Das war meine Schuld, da ich vor zwanzig Jahren nicht schon wusste, was ich heute über Architektur und Programmierung weiß, und die Konsequenzen von Design-Entscheidungen nicht absehen konnte.

Alberto Brandolini beschreibt eine solche Situation wie folgt:

The dark secret of the Dungeon Master is that he knows every trap in the existing legacy software, because he was the one to leave the traps around. [...] Knowledge, in the form of accidental complexity starts accumulating in the head of the Dungeon Master, and silently grows.

– Alberto Brandolini: „ The rise and fall of the Dungeon Master

Es tröstet mich ein wenig, dass ich damit nicht alleine bin. Erich Gamma und Kent Beck, die ursprünglichen Autoren von JUnit, haben ähnliche Probleme:

The problem is in software design often the consequences of your decisions don't become apparent for years. One of the advantages of having to live with JUnit for 8 years, is now we can look back and see which decisions we made worked nicely and which we would have done differently.

– Kent Beck: „ We thought we were just programming on an airplane

Mittlerweile hat sich die Situation deutlich gebessert. Zum einen tragen die jährlichen Major-Versionen, die sowohl Funktionalität aus PHPUnit entfernen beziehungsweise so verändern, dass Implementierung und Verwendung einfacher werden, als auch Unterstützung für alte PHP-Versionen entfernen dazu bei, dass die Codebasis von PHPUnit verständlicher und wartbarer wird. Zum anderen habe die regelmäßig Code-Sprints der letzten Jahre dazu beigetragen, dass neue Entwickler zum PHPUnit-Projekt gekommen sind, die mich nun bei meiner Arbeit unterstützen.

Marco Pivetta fasst dies wie folgt zusammen:

[PHPUnit] has been very stale for ages, and is now moving much faster thanks to people now willing to invest time in it (thanks to constant rework and cleaning)

Marco Pivetta auf Twitter

PHPUnit 9

Nachdem das „Warum“ der jährlichen Änderungen nun hoffentlich klarer geworden ist, ist es Zeit, dass wir uns die Änderungen von PHPUnit 9 genauer anschauen.

PHP 7.3

PHPUnit 9 benötigt PHP 7.3 (oder neuer). Die aktive Unterstützung von PHP 7.2 durch das PHP-Projekt endete am 30. November 2019. Stand heute werden nur PHP 7.3 und PHP 7.4 offiziell und aktiv unterstützt. PHPUnit 9 wird auf PHP 7.3 und PHP 7.4 unterstützt. Wenn Ende des Jahres PHP 8 erscheint, so wird auch diese Version sehr wahrscheinlich von PHPUnit 9 unterstützt werden.

Wenn Sie noch PHP 7.2 einsetzen dann sollten Sie jetzt mit der Migration auf eine aktuelle PHP-Version, idealerweise PHP 7.4, beginnen. Das PHP-Projekt bietet keine Bugfixes mehr für PHP 7.2 und sicherheitskritische Fehler werden nur noch bis zum 30. November 2020 behoben. Ein langfristiges Ziel sollte sein, das Aktualisieren der eingesetzten PHP-Version als regelmäßige Aufgabe – und nicht als spezielles Projekt, das nur alle Jubeljahre angegangen wird – zu begreifen. Der entsprechende Aktualisierungsprozess sollte sich am aktiven Support des PHP-Projekts für aktuelle PHP-Versionen ausrichten .

Wenn Sie PHP 7.3 noch nicht verwenden können oder wollen, dann können Sie natürlich auch PHPUnit 9 noch nicht verwenden. Noch stellt dies kein allzu großes Problem dar, da PHPUnit 8.5, das noch mit PHP 7.2 funktioniert, bis Februar 2021 mit Bugfixes unterstützt wird . Allerdings verpassen Sie natürlich sämtliche Verbesserungen neuerer Versionen von PHP und PHPUnit.

assertEquals() (und verwandte Zusicherungen)

assertEquals() ist die am häufigsten genutzte Zusicherungsmethode. Leider, denn assertSame() ist fast immer die bessere Alternative. Im Laufe der Jahre wurden assertEquals() viele optionale Parameter hinzugefügt. Einige von diesen konnten nicht zusammen genutzt werden, was immer wieder zu Edge Case-Fehler in der zugrundeliegenden Implementierung geführt hat. Das Hauptproblem mit einer langen Liste von optionalen Parametern ist, dass man die ersten vier angeben muss, wenn man beispielsweise den fünften verwenden will. Und wer kann sich schon merken, wofür all diese Parameter gut sind? Ich jedenfalls nicht.

Um Abhilfe zu schaffen, wurden in PHPUnit 7.5 spezialisierte Alternativen eingeführt und in PHPUnit 8 die folgenden optionalen Parameter von assertEquals() abgekündigt:

Diese Parameter, von denen übrigens $maxDepth schon lange keinen Effekt mehr hatte, wurden nun in PHPUnit 9 entfernt.

Der optionale Parameter $delta konnte genutzt werden, um zwei Zahlenwerte so miteinander zu vergleichen, dass sie auch noch als gleich betrachtet werden, wenn sie – bis auf ein Delta – nur fast gleich sind. Beispielsweise stellt

$this->assertEquals(1.0, 1.1, '', 0.1);

sicher, dass der tatsächliche Wert 1.1 mit einem Delta von 0.1 dem erwarteten Wert 1.0 entspricht. Dies kann und muss nun einfacher und lesbarer mit

$this->assertEqualsWithDelta(1.0, 1.1, 0.1);

ausgedrückt werden.

Der optionale Parameter $canonicalize konnte genutzt werden, um sowohl den erwarteten als auch den tatsächlichen Wert vor ihrem Vergleich in eine kanonische (einheitliche) Form zu bringen. Für Werte vom Typ Array wird hierfür beispielsweise eine Sortierung durchgeführt. Da $canonicalize der sechste Parameter (sowie der vierte optionale Parameter nach den beiden nicht optionalen Parametern $expected und $actual ) war, musste man eine solche Zusicherung in der Vergangenheit mit

$this->assertEquals($expected, $actual, '', 0.0, 10, true);

ausdrücken. Dies kann und muss nun einfacher und lesbarer mit

$this->assertEqualsCanonicalizing($expected, $actual);

ausgedrückt werden.

Der optionale Parameter $ignoreCase konnte genutzt werden, um Groß- und Kleinschreibung für den Vergleich von erwartetem und tatsächlichem Wert zu ignorieren. Eine solche Zusicherung konnte man in der Vergangenheit nur mit

$this->assertEquals($expected, $actual, '', 0.0, 10, false, true);

ausdrücken. Dies kann und muss nun einfacher und lesbarer mit

$this->assertEqualsIgnoringCase($expected, $actual);

ausgedrückt werden.

Der Vollständigkeit halber sei erwähnt, dass alles, was oben ausgeführt wurde, auch für assertNotEquals() , das Inverse zu assertEquals() , gilt.

Analog zum Entfernen der optionalen Parameter $canonicalize und $ignoreCase aus der API von assertEquals() wurden die entsprechenden optionalen Parameter ebenfalls aus der API von assertFileEquals() und assertStringEqualsFile() entfernt. An ihrer Stelle gibt es nun spezialisierte Zusicherungsmethoden:

Dies gilt natürlich auch für assertFileNotEquals() und assertStringNotEqualsFile() , die Inverse zu assertFileEquals() und assertStringEqualsFile() .

assertContains()

Im Laufe der Zeit waren optionale Parameter auch zur assertContains() Zusicherungsmethode hinzugefügt worden. Darüber hinaus wurde ihr Anwendungsbereich von Arrays sowie Objekten, die Iterator implementieren, auf Strings ausgeweitet. Immer wieder kam es dabei zu Problemen, die sich auch der Umsetzung verschiedener, teilweise miteinander in Konflikt stehenden, Use Cases in derselben Codeeinheit ergaben.

Um diese Probleme zu beheben, wurden in PHPUnit 7.5 spezialisierte Alternativen eingeführt, die nur auf Strings operieren. In PHPUnit 8 wurden die optionalen Parameter $checkForObjectIdentity , $checkForNonObjectIdentity und $ignoreCase abgekündigt. Darüber hinaus wurde die Verwendung von assertContains() mit String-Haystacks ebenfalls abgekündigt. Die genannten Parameter sowie die Möglichkeit, assertContains() mit String-Haystacks zu verwenden, wurden nun in PHPUnit 9 entfernt.

assertContains() wurde so geändert, dass immer ein typsicherer Vergleich (mit dem === Operator) durchgeführt wird. Ist ein Vergleich gewünscht, der nicht typsicher (mit dem == Operator) ist, so ist die neue assertContainsEquals() Methode zu verwenden. Hier sind einige Beispiele:

Der Vollständigkeit halber sei erwähnt, dass alles, was oben ausgeführt wurde, auch für assertNotContains() , das Inverse zu assertContains() , gilt.

assertInternalType()

Die Zusicherungsmethode assertInternalType() konnte benutzt werden, um sicherzustellen, dass eine Variable einen Wert von einem bestimmten Typ enthält, der nicht benutzerdefiniert ist. Hier ist ein Beispiel, das zeigt, wie man sicherstellen konnte, dass eine Variable einen Wert vom Typ Array enthält:

$this->assertInternalType('array', $variable);

Ein wichtiger Aspekt der oben gezeigten Zusicherung ist in einem Parameter, 'array' , versteckt. Das ist problematisch. Beispielsweise kann eine IDE keine automatische Vervollständigung für das Wort 'array' anbieten. Darüber hinaus gibt es keinen Schutz vor Tippfehlern im String 'array' . Es ist ja schließlich nur ein String.

Um dieses Problem zu beheben, wurden in PHPUnit 7.5 spezialisierte Alternativen eingeführt. Diese neuen Zusicherungsmethoden verstecken keine wesentliche Information mehr in einem String-Parameter, sondern machen ihre Verantwortlichkeit explizit in ihrem Methodennamen:

$this->assertIsArray($variable);

Natürlich bedeutet eine explizitere API, dass es mehr Methoden gibt. Allerdings ist jede dieser Methoden einfacher, da sie nur genau einen Use Case implementiert. Dies führt zu Code, der einfacher zu verstehen ist. Er ist darüber hinaus einfacher zu schreiben, da die IDE nun automatische Vervollständigung bieten kann.

Der Vollständigkeit halber sei erwähnt, dass alles, was oben ausgeführt wurde, auch für assertNotInternalType() , das Inverse zu assertInternalType() , gilt.

assertArraySubset()

Die Methode assertArraySubset() war eine konstante Quelle von Verwirrung und Frustration, was man in vielen Tickets im Issue-Tracker von PHPUnit nachlesen kann.

Diese Situation war entstanden, weil ich den ursprünglichen Pull Request nicht gründlich genug geprüft hatte. Ich selbst hatte keine Verwendung für die vorgeschlagene Zusicherung. Auf den ersten Blick sah die Implementierung so aus, als würde sie den Use Case des Entwicklers, der die Zusicherung vorschlug, erfüllen. Im Laufe der Jahre habe ich immer wieder Pull Requests akzeptiert, von denen ich dachte, dass sie nur Fehler in der Implementierung von assertArraySubset() beheben. Allerdings fügten einige dieser Pull Requests neue Funktionalität hinzu, die teilweise im Konflikt zu anderen, bereits existierenden und unterstützten Anwendungsfällen von assertArraySubset() stand. Dies hätte nicht passieren dürfen. Allerdings konnte diese zusätzliche Funktionalität auch nicht mehr entfernt werden, ohne dass es zu einem Bruch der Abwärtskompatibilität gekommen wäre.

Während der Arbeit an PHPUnit 8 bin gelangte ich zu der Erkenntnis, dass die Probleme von assertArraySubset() nicht behoben werden können, ohne die Abwärtskompatibilität zu brechen. Ich beschloss daher, assertArraySubset() abzukündigen und die Methode in PHPUnit 9 zu entfernen. Dies führt zu einem Bruch der Abwärtskompatibilität, der explizit und offensichtlich ist. Jegliche Änderung am Verhalten von assertArraySubset() wäre auch ein Bruch der Abwärtskompatibilität, allerdings wäre dieser dann implizit und nicht offensichtlich, gewesen.

Wenn man die Funktionalität von assertArraySubset() benötigt, dann kann man diese durch Installation einer Erweiterung für PHPUnit von Rafael Dohms wiederherstellen.

Zusicherungen und nicht öffentliche Eigenschaften

Zu große Objekte mit problematischen Abhängigkeiten sind in Legacy Code üblich. Solche Objekte müssen häufig indirekt getestet werden, indem man ihren nicht öffentlichen Zustand betrachtet. In der Vergangenheit waren Zusicherungen wie assertAttributeEquals() eingeführt worden, die auf nicht öffentlichen Eigenschaften arbeiten. Diese Zusicherungen waren ausschließlich für das Testen von Legacy Code eingeführt worden. Ihre Verwendung war nie als Best Practice, schon gar nicht für neuen Code, empfohlen.

Leider wurden diese Zusicherungen allzu oft missbraucht, um neuen Code zu testen, bei dem man die Testbarkeit vernachlässigt hat. Es ist ein Fehler, den Inhalt von nicht öffentlichen Eigenschaften testen zu wollen. Anstatt den Zustand eines Objektes testen zu wollen, sollte vielmehr das Verhalten eines Objekts getestet werden.

Es hat sich im Laufe der Jahre gezeigt, dass die Bereitstellung von Zusicherungen wie assertAttributeEquals() zu schlechten Testpraktiken geführt hat. Für PHPUnit 8 hatte ich mich daher entschlossen, diese Zusicherungen abzukündigen und in PHPUnit 9 wurden sie nun entfernt.

Nicht öffentliche Methoden dürfen nicht direkt getestet werden, indem man ihre Sichtbarkeit unter Zuhilfenahme der Reflection API umgeht. Der private Zustand eines Objekts darf ebenfalls nicht in einem Test betrachtet werden. Diese beiden Praktiken führen zu einer engen Kopplung von Testcode an den getesteten Code und damit dazu, dass man Implementierungsdetails anstelle der API testet. Sobald sich die Implementierung ändert, geht der Test kaputt, da er sich auf private Implementierungsdetails verlässt.

Testen von Ausnahmen

Lange Zeit galt es als Best Practice, Ausnahmen zu testen, indem man die erwartete Ausnahme mithilfe der @expectedException Annotation am Test dokumentiert. Dieser Ansatz hat zwei fundamentale Probleme:

Ich glaube mittlerweile, dass die Verwendung von Annotationen für das Erwarten von Ausnahmen mehr schadet, als dass sie nutzt. Daher kündigte ich die Verwendung von Annotationen wie @expectedException in PHPUnit 8 ab und die ensprechende Funktionalität wurde nun in PHPUnit 9 entfernt.

Aus

/**
 * @expectedException vendor\project\MyException
 */
public   function   testSomething ( ) :   void
{
     // ...
}

wird beispielsweise

public   function   testSomething ( ) :   void
{
     $this -> expectException ( MyException :: class ) ;
 
     // ...
}

Wo wir gerade beim Testen von Ausnahmen sind: in PHPUnit 9 gibt es die Methode expectExceptionMessageRegExp() nicht mehr. Sie wurde bereits in PHPUnit 8 abgekündigt. Stattdessen ist expectExceptionMessageMatches() zu verwenden.

Testen von PHP-Fehlern

Die Klassen Deprecated , Error , Notice und Warning im PHPUnit\Framework\Error Namespace sind interne Implementierungsdetails von PHPUnit, mit denen entsprechende PHP-Fehlersituationen repräsentiert werden. Dennoch musste man diese privaten Details von PHPUnit in seinen Tests bislang verwenden, um beispielsweise testen zu können, dass während eines Tests ein bestimmter PHP-Fehler auftritt:

<?php  declare ( strict_types = 1 ) ;
use   PHPUnit\Framework\TestCase ;
use   PHPUnit\Framework\Error\Notice ;
 
final   class   Test   extends   TestCase
{
     public   function   testOne ( )
     {
         $this -> expectException ( Notice :: class ) ;
 
         $a   =   $b ;
     }
}

Mittlerweile gibt es spezialisierte Methoden, mit denen das erwartete Auftreten eines PHP-Fehlers dokumentiert werden kann:

Die Verwendung von expectException() mit Deprecated , Error , Notice , Warning ist seit PHPUnit 9 abgekündigt und wird mit PHPUnit 10 nicht mehr möglich sein. Das oben gezeigte Beispiel muss nun wie folgt formuliert werden:

<?php  declare ( strict_types = 1 ) ;
use   PHPUnit\Framework\TestCase ;
 
final   class   Test   extends   TestCase
{
     public   function   testOne ( )
     {
         $this -> expectNotice ( ) ;
 
         $a   =   $b ;
     }
}

Test-Stubs und Mock-Objekte

Mit den Methoden getMockBuilder() , createMock() , createConfiguredMock() und createPartialMock() können Objekte erzeugt werden, die anstelle einer echten Abhängigkeit beziehungsweise anstelle eines echten kollaborierenden Objekts in einem Test verwendet werden können. Hierzu genügt es, den Typ des Objekts, entweder in Form eines Schnittstellennamens oder eines Klassennamens, anzugeben: $this->createMock(Service::class) erzeugt ein Objekt, das so aussieht (denselben Typ hat) wie ein Service -Objekt, den Code des echten Objekts aber nicht ausführt.

Im Laufe der Jahre wurde der Code-Generator, der hinter den Kulissen von PHPUnit dafür sorgt, dass die oben gezeigte API für die Erzeugung von Mock-Objekten funktioniert, dahingehend erweitert, dass mehr als ein Typ für die Erzeugung eines Mock-Objektes angegeben werden kann. Da es fast immer ein Zeichen für schlechtes Softwaredesign ist, wenn man diese Funktionalität benötigt, und weil diese Funktionalität die Implementierung des Code-Generators für Mock-Objekte (unnötig) kompliziert macht, ist die Angabe von mehr als einem Typ für die oben aufgezählten Methoden in PHPUnit 9 abgekündigt. In PHPUnit 10 wird diese Funktionalität nicht mehr zur Verfügung stehen.

CLI Test Runner

Die Möglichkeit, auf der Kommandozeile phpunit ServiceTest.php sowie alternativ phpunit ServiceTest aufrufen zu können, hat zu unnötig kompliziertem Code geführt, der schwer verständlich und sehr anfällig für Fehler geworden war. Der phpunit ServiceTest Aufruf war daher bereits in PHPUnit 8 abgekündigt worden und wurde nun in PHPUnit 9 entfernt.

Lasst uns von vorne anfangen!

Jede Entwicklerin und jeder Entwickler kennt den Wunsch „Lasst uns das alles wegwerfen und auf der grünen Wiese neu anfangen!“. Auch mir ist dieser Wunsch nicht fremd und so habe ich im Laufe der Jahre mehrmals darüber nachgedacht, PHPUnit als Ganzes oder in Teilen (beispielsweise „nur“ der Testrunner) von Grund auf neu zu entwickeln. Zuletzt habe ich darüber vor zwei Jahren nachgedacht und meine Gedanken und Ideen in einem Vortrag geteilt. Ich habe mich bislang aber stets gegen eine solchen revolutionären Ansatz entschieden. Zum einen, weil er sehr viel Arbeit erfordern würde, die dazu noch „an einem Stück“ geleistet werden müsste. Vor allem aber, weil es bei einem solchen Neuanfang fast unausweichlich wäre, dass sämtliche Tests, die PHPUnit verwenden, zumindest minimal – wahrscheinlich aber signifikant – angepasst werden müssten.

Für mich ergibt daher nur ein evolutionärer Ansatz Sinn, bei dem Schritt für Schritt Teile von PHPUnit ausgetauscht oder verbessert werden. Während eines Code Sprints im letzten Jahr hat beispielsweise Marco Pivetta sehr alten und unsauberen Code entfernt, der sich um das Parsen der Annotationen in PHPUnit-Tests kümmert. Der neue Code hat weniger Fehler und ist viel einfacher zu verstehen und anzupassen. Diese Arbeit ist bereits in PHPUnit 8 eingeflossen. Für PHPUnit 9 wurde nun der Code neu geschrieben, der sich um das Laden XML-Konfigurationsdatei kümmert. Für 2020 stehen die Umstellung sämtlicher Erweiterungsmöglichkeiten, insbesondere was das Logging anbelangt, auf dem Plan. Seit dem letzten Code Sprint wird dieses Thema von Ewout Pieter den Ouden, Andreas Möller und Arne Blankerts bearbeitet. Ihre Arbeit wird wahrscheinlich in PHPUnit 9.1 oder PHPUnit 9.2 einfließen.