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.