Wer kennt das nicht? Die neue Extension funktioniert perfekt in der Entwicklungsumgebung, aber nach dem nächsten TYPO3 Update hagelt es plötzlich Fehlermeldungen. „Hätten wir mal automatisierte Tests geschrieben“, denkt ihr euch dann. Genau hier kommt das TYPO3 Testing Framework ins Spiel – und wir zeigen euch, wie ihr es meistert.
Bei Never Code Alone haben wir uns über 15 Jahre auf Softwarequalität, Open Source und Remote Consulting spezialisiert. Tests sind dabei kein notwendiges Übel, sondern euer Sicherheitsnetz für nachhaltigen Code. Lasst uns gemeinsam durchgehen, wie ihr das TYPO3 Testing Framework optimal nutzt.
Warum Tests für TYPO3 Extensions unverzichtbar sind
Die TYPO3 Community hat über die Jahre ein beeindruckendes Testing Framework aufgebaut. Der Core selbst enthält über 10.000 Unit Tests – und das hat seinen Grund. Jeder Test ist wie ein kleiner Wächter, der sicherstellt, dass euer Code auch nach Updates noch funktioniert.
Besonders spannend: Tests zeigen oft, welche Teile des Frameworks nicht optimal modelliert sind. Ein typisches Beispiel ist die BackendUtility
Klasse – wer versucht hat, Systeme mit dieser Abhängigkeit zu testen, kennt den Schmerz. Die gute Nachricht: Mit jedem Release wird das besser.
Die 10 häufigsten Fragen zum TYPO3 Testing Framework – direkt beantwortet
1. Wie richte ich das TYPO3 Testing Framework für meine Extension ein?
Der erste Schritt ist überraschend einfach. Ihr benötigt nur zwei Composer-Abhängigkeiten:
composer require --dev
"typo3/testing-framework:^8.0.9"
"phpunit/phpunit:^10.5"
Die Versionen hängen von eurer TYPO3-Version ab. Für TYPO3 v13/14 nutzt ihr Testing Framework 9.x, für v12 die Version 8.x und für v11 die 7.x. Nach der Installation kopiert ihr die Basis-Konfiguration:
vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml
vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsBootstrap.php
Diese Dateien legt ihr in eurem Build/phpunit/
Ordner ab und passt die Pfade an. Fertig!
2. Welche PHPUnit-Version passt zu meiner TYPO3-Installation?
Die Kompatibilitätsmatrix ist entscheidend für funktionierende Tests:
- TYPO3 v13/14: PHPUnit 10.5+ mit Testing Framework 9.x (PHP 8.2+)
- TYPO3 v12: PHPUnit 10.x mit Testing Framework 8.x (PHP 8.1+)
- TYPO3 v11: PHPUnit 9.x mit Testing Framework 7.x (PHP 7.4/8.0+)
Ein Tipp aus der Praxis: Nutzt immer die neueste Patch-Version der jeweiligen PHPUnit-Major-Version. Das erspart euch viele Kompatibilitätsprobleme.
3. Wie strukturiere ich meine Test-Dateien richtig?
TYPO3 folgt einer klaren Konvention, die euer Leben deutlich einfacher macht:
typo3conf/ext/meine_extension/
├── Classes/
│ └── Domain/
│ └── Service/
│ └── MyService.php
└── Tests/
└── Unit/
└── Domain/
└── Service/
└── MyServiceTest.php
Die Testklasse spiegelt die Struktur der getesteten Klasse wider und erhält das Suffix Test
. PhpStorm versteht diese Struktur übrigens perfekt – mit STRG+Shift+T springt ihr direkt zwischen Klasse und Test hin und her.
4. Wie nutze ich Data Providers für effiziente Tests?
Data Providers sind euer bester Freund für parametrisierte Tests. Statt zehn ähnliche Tests zu schreiben, nutzt ihr einen Test mit verschiedenen Eingabewerten:
#[DataProvider('provideFilterData')]
#[Test]
public function filterByValueWorksCorrectly($needle, $haystack, $expected): void
{
self::assertEquals(
$expected,
ArrayUtility::filterByValue($needle, $haystack)
);
}
public static function provideFilterData(): array
{
return [
'empty array' => ['banana', [], []],
'string found' => ['banana', ['apple', 'banana'], [1 => 'banana']],
'string not found' => ['orange', ['apple', 'banana'], []],
];
}
Pro-Tipp: Gebt euren Test-Cases sprechende Namen. Das macht Debugging bei fehlschlagenden Tests viel angenehmer.
5. Was ist der Unterschied zwischen Unit und Functional Tests?
Unit Tests testen isolierte Einheiten eures Codes ohne Datenbank oder Framework-Abhängigkeiten. Sie sind schnell (unter 1 Sekunde pro Test) und fokussiert:
class MyServiceTest extends UnitTestCase
{
#[Test]
public function calculateReturnCorrectSum(): void
{
$service = new MyService();
self::assertEquals(4, $service->add(2, 2));
}
}
Functional Tests hingegen testen das Zusammenspiel mit der Datenbank und dem TYPO3 Framework:
class MyRepositoryTest extends FunctionalTestCase
{
protected array $testExtensionsToLoad = [
'typo3conf/ext/my_extension',
];
#[Test]
public function findByUidReturnsCorrectRecord(): void
{
$this->importCSVDataSet(__DIR__ . '/Fixtures/records.csv');
$repository = $this->get(MyRepository::class);
$result = $repository->findByUid(1);
self::assertEquals('Test Record', $result->getTitle());
}
}
6. Wie mocke ich Dependencies in TYPO3 richtig?
Mocking ist essentiell für isolierte Unit Tests. TYPO3 bietet verschiedene Ansätze:
#[Test]
public function serviceUsesRepository(): void
{
// Mock erstellen
$repositoryMock = $this->createMock(MyRepository::class);
$repositoryMock->expects($this->once())
->method('findAll')
->willReturn(['item1', 'item2']);
// Dependency Injection
$service = new MyService();
$service->injectRepository($repositoryMock);
// Test ausführen
$result = $service->processAll();
self::assertCount(2, $result);
}
Für statische Dependencies nutzt ihr GeneralUtility::addInstance()
:
$iconFactoryMock = $this->createMock(IconFactory::class);
GeneralUtility::addInstance(IconFactory::class, $iconFactoryMock);
7. Wie führe ich einzelne Tests gezielt aus?
Während der Entwicklung wollt ihr nicht immer die komplette Test-Suite laufen lassen. PHPUnit bietet dafür den --filter
Parameter:
# Einzelne Test-Methode
vendor/bin/phpunit -c Build/phpunit/UnitTests.xml
--filter "testCalculateReturnCorrectSum"
# Komplette Test-Klasse
vendor/bin/phpunit -c Build/phpunit/UnitTests.xml
Tests/Unit/Service/MyServiceTest.php
# Alle Tests mit bestimmtem Pattern
vendor/bin/phpunit -c Build/phpunit/UnitTests.xml
--filter "Repository"
In DDEV könnt ihr euch Custom Commands anlegen für noch schnelleren Zugriff.
8. Sollte ich protected Methoden direkt testen?
Die kurze Antwort: In den meisten Fällen nein. Protected Methoden sollten über die öffentliche API getestet werden. Wenn das nicht möglich ist, hinterfragt euer Design.
Falls es doch nötig ist (z.B. bei Legacy Code), nutzt Reflection:
#[Test]
public function protectedMethodWorksCorrectly(): void
{
$service = new MyService();
$method = new ReflectionMethod($service, 'protectedMethod');
$method->setAccessible(true);
$result = $method->invoke($service, 'parameter');
self::assertEquals('expected', $result);
}
Besser ist aber oft ein Refactoring zu einer testbaren Struktur.
9. Welche Fallstricke sollte ich beim Testing vermeiden?
Nicht testen: „Glue Code“ – Controller-Actions, die nur Daten von A nach B schieben, brauchen keine Unit Tests. Der Test würde nur das Framework testen.
Zu komplexe Tests schreiben – Tests sollten einfacher sein als der getestete Code. Keine abstrakten Test-Hierarchien, keine fancy Patterns. Copy & Paste ist in Tests völlig okay!
PHP Notices ignorieren – Das Testing Framework ist standardmäßig so konfiguriert, dass es bei PHP Notices fehlschlägt. Das ist gut so! Fixt die Notices, statt sie zu unterdrücken.
Falsche Test-Granularität – Ein Test sollte genau eine Sache testen. Mehrere Assertions sind okay, aber sie sollten alle denselben Aspekt prüfen.
10. Wie integriere ich Tests in meine CI/CD Pipeline?
Für GitHub Actions erstellt ihr eine .github/workflows/tests.yml
:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- run: composer install
- run: vendor/bin/phpunit -c Build/phpunit/UnitTests.xml
Für GitLab CI nutzt ihr .gitlab-ci.yml
:
test:
image: php:8.2-cli
script:
- composer install
- vendor/bin/phpunit -c Build/phpunit/UnitTests.xml
Pro-Tipp: Nutzt das runTests.sh
Script aus dem TYPO3 Core als Vorlage. Es bietet viele Optionen für verschiedene Test-Szenarien.
Best Practices aus über 15 Jahren Erfahrung
Nach unzähligen Projekten haben wir bei Never Code Alone einige goldene Regeln entwickelt:
1. Testet früh und kontinuierlich – Der beste Zeitpunkt für den ersten Test ist direkt nach dem ersten Feature.
2. Test-First bei Bugs – Schreibt erst einen (fehlschlagenden) Test, der den Bug reproduziert, dann fixt ihn.
3. Nutzt Type Declarations – PHP 8+ Type Hints machen eure Tests robuster und die IDE-Unterstützung besser.
4. Fixtures sinnvoll organisieren – CSV-Dateien für Testdaten in Tests/Functional/Fixtures/
ablegen und sprechend benennen.
5. Tests als Dokumentation – Gute Tests zeigen, wie eure Klassen verwendet werden sollen.
Der Einstieg: Klein anfangen, kontinuierlich ausbauen
Ihr müsst nicht sofort 100% Test Coverage erreichen. Startet mit den kritischen Geschäftslogik-Komponenten. Ein Repository mit komplexen Queries? Perfekt für Functional Tests. Ein Service mit Berechnungen? Ideal für Unit Tests.
Das Wichtigste: Fangt an! Jeder Test macht eure Extension robuster und euer Leben als Entwickler entspannter.
Unser Angebot: Gemeinsam zu besserer Code-Qualität
Bei Never Code Alone unterstützen wir Teams dabei, Testing in ihren Entwicklungsprozess zu integrieren. Ob Workshop vor Ort, Remote-Schulung oder Pair Programming – wir finden den passenden Weg für euch.
Unsere Spezialität: Die bewährte Funktastatur-Methode, bei der jeder im Team aktiv mitmacht und das Wissen direkt in die Finger bekommt. Keine trockene Theorie, sondern praktisches Arbeiten an eurem echten Code.
Interessiert? Meldet euch direkt bei uns: roland@nevercodealone.de
Wir freuen uns darauf, gemeinsam mit euch die Qualität eurer TYPO3-Projekte auf das nächste Level zu heben. Denn gut getesteter Code ist kein Luxus – er ist die Basis für nachhaltigen Projekterfolg.
Never Code Alone – Über 15 Jahre Expertise in Softwarequalität, Open Source und Remote Consulting. Wir machen Developer und Entscheider zu einem unschlagbaren Team.