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

&lt;?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);
    }
}
?&gt;

vendor/bin/phpunit

durchsucht standardmäßig tests/ und
gibt ein farbiges Ergebnis: grün ✔︎ oder rot ✖︎.

3. PHPUnit Konfiguration – phpunit.xml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;phpunit bootstrap="vendor/autoload.php"
         colors="true"
         stopOnFailure="false"&gt;

  &lt;testsuites&gt;
    &lt;testsuite name="Unit"&gt;
      &lt;directory suffix="Test.php"&gt;tests/Unit&lt;/directory&gt;
    &lt;/testsuite&gt;
  &lt;/testsuites&gt;

&lt;/phpunit&gt;

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

  1. Eine Testklasse pro Produktionsklasse, klare Namens­konvention.
  2. Tests unabhängig – keine Reihenfolge, keine globalen States.
  3. arrange → act → assert Muster (gegeben / wenn / dann).
  4. 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 Sicherheits­gurt für deine
PHP‑Anwendung: Sie verhindern Regressionen, erleichtern Refactoring und bilden
eine lebendige Dokumentation deines Codes.