Automatisierte Tests in PHP – PHPUnit und Pest
Erste Schritte: Automatisierte Tests in PHP – PHPUnit und Pest
Automatisierte Tests stellen sicher, dass Änderungen am Code keine unerwünschten
Nebenwirkungen haben.
In der PHP‑Welt sind PHPUnit (De‑facto‑Standard seit Jahren) und
Pest (fluente Syntax + PHPUnit‑Engine) die populärsten
Frameworks.
Dieses Tutorial zeigt Installation, Beispiel‑Tests und CLI‑Workflows –
alle Code‑Snippets stehen im <pre>‑Block.
1. Installation via Composer
# PHPUnit (klassisch) composer require --dev phpunit/phpunit ^10 # Pest (baut intern auf PHPUnit auf) composer require --dev pestphp/pest ^2
- Pakete landen in require-dev – nicht in Produktion.
- Pest installiert eigene CLI (
vendor/bin/pest
), zieht PHPUnit automatisch mit.
2. PHPUnit – Erster Testfall
<?php // tests/Unit/TaskTest.php use App\Model\Task; use PHPUnit\Framework\TestCase; class TaskTest extends TestCase { public function test_title_must_not_be_empty(): void { $this->expectException(InvalidArgumentException::class); $task = new Task(); $task->setTitle(''); // sollte Exception werfen } public function test_status_defaults_to_open(): void { $task = new Task(); $this->assertSame('open', $task->status); } } ?>
vendor/bin/phpunit
durchsucht standardmäßig tests/ und
gibt ein farbiges Ergebnis: grün ✔︎ oder rot ✖︎.
3. PHPUnit Konfiguration – phpunit.xml
<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php" colors="true" stopOnFailure="false"> <testsuites> <testsuite name="Unit"> <directory suffix="Test.php">tests/Unit</directory> </testsuite> </testsuites> </phpunit>
4. Pest – eine expressivere Syntax
# tests/Feature/ExampleTest.php use App\Model\Task; it('creates a task', function () { $task = new Task(); $task->title = 'Hello'; expect($task->title)->toBe('Hello'); }); it('throws when title empty') ->expect(fn () => (new Task())->setTitle('')) ->toThrow(InvalidArgumentException::class);
CLI‐Aufruf:
vendor/bin/pest
Pest verwendet „higher‑order tests“ & expect()‑Fluent‑API –
weniger Boilerplate.
5. Datenbank‑Tests – Memory‑SQLite (Beispiel)
protected function setUp(): void { $pdo = new PDO('sqlite::memory:'); $pdo->exec(file_get_contents(__DIR__.'/../../database/create_tasks.sql')); App\Core\Database::mock($pdo); // eigene Helper‑Methode }
So testest du Models ohne echte MySQL‑Instanz – schnell & isoliert.
6. Test Doubles – Mock & Stub
use PHPUnit\Framework\MockObject\MockObject; $logger = $this->createMock(LoggerInterface::class); $logger->expects($this->once()) ->method('info') ->with('Task created'); $service = new TaskService($logger); $service->create('Demo');
7. CLI‑Shortcuts in composer.json
{ "scripts": { "test": "phpunit", "pest": "pest --coverage" } }
Jetzt genügt
composer test
oder
composer pest
.
8. Continuous Integration ( GitHub Actions )
# .github/workflows/test.yml name: Tests on: [ push, pull_request ] jobs: phpunit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: shivammathur/setup-php@v2 with: { php-version: '8.3', coverage: pcov } - run: composer install --no-progress --no-suggest - run: composer test # oder composer pest
9. Code‑Coverage & Mutation‑Tests
- pcov oder xdebug für Coverage (
--coverage-html build/coverage
).
- Infection PHP prüft Test‑Qualität via Mutation‑Testing.
10. Best Practices
- Eine Testklasse pro Produktionsklasse, klare Namenskonvention.
- Tests unabhängig – keine Reihenfolge, keine globalen States.
- arrange → act → assert Muster (gegeben / wenn / dann).
- Schnelle Unit‑Tests lokal, langsamere Integration‑Tests im CI.
Fazit
PHPUnit bietet mächtige Assertions, Mocks & Konfig‑Flexibilität,
Pest liefert syntaktischen Zucker oben drauf.
Egal welches Tool – automatisierte Tests sind der Sicherheitsgurt für deine
PHP‑Anwendung: Sie verhindern Regressionen, erleichtern Refactoring und bilden
eine lebendige Dokumentation deines Codes.