Steuerkram/tests/TestCase.php

115 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
/**
* Basis-Testklasse für Steuerkram-Tests.
*
* Jeder Test läuft in einer MySQL-Transaktion, die am Ende zurückgerollt wird.
* Damit bleibt die Datenbank nach dem Test unverändert.
*/
abstract class TestCase
{
private static int $totalPassed = 0;
private static int $totalFailed = 0;
private static array $allFailures = [];
protected int $passed = 0;
protected int $failed = 0;
protected array $failures = [];
protected string $testName = '';
abstract public function run(): void;
protected function setUp(): void
{
\Steuerkram\Database::beginTransaction();
}
protected function tearDown(): void
{
try {
\Steuerkram\Database::rollBack();
} catch (\Throwable) {
// Transaktion war evtl. nicht aktiv
}
}
protected function assert(bool $condition, string $message): void
{
if ($condition) {
$this->passed++;
echo " \e[32m✓\e[0m {$message}\n";
} else {
$this->failed++;
$this->failures[] = "[{$this->testName}] {$message}";
echo " \e[31m✗\e[0m {$message}\n";
}
}
protected function assertEquals(mixed $expected, mixed $actual, string $message): void
{
$this->assert(
$expected == $actual,
"{$message} (erwartet: " . json_encode($expected) . ", erhalten: " . json_encode($actual) . ")"
);
}
protected function assertGreaterThan(int $min, mixed $actual, string $message): void
{
$this->assert($actual > $min, "{$message} (Wert: {$actual}, muss > {$min})");
}
protected function assertNotEmpty(mixed $value, string $message): void
{
$this->assert(!empty($value), $message);
}
/**
* Liefert Konto-ID anhand der Kontonummer oder null wenn nicht gefunden.
*/
protected function findAccountId(string $kontonummer): ?int
{
$row = \Steuerkram\Database::fetchOne(
'SELECT id FROM accounts WHERE kontonummer = ?',
[$kontonummer]
);
return $row ? (int)$row['id'] : null;
}
public function execute(): array
{
echo "\n\e[1m{$this->testName}\e[0m\n";
$this->setUp();
try {
$this->run();
} catch (\Throwable $e) {
$this->failed++;
$msg = "[{$this->testName}] Ausnahme: " . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine();
$this->failures[] = $msg;
echo " \e[31m✗\e[0m Unerwartete Ausnahme: " . $e->getMessage() . "\n";
} finally {
$this->tearDown();
}
$status = $this->failed === 0
? "\e[32mBestanden ({$this->passed}/{$this->passed})\e[0m"
: "\e[31m{$this->failed} Fehler, {$this->passed} OK\e[0m";
echo " → {$status}\n";
self::$totalPassed += $this->passed;
self::$totalFailed += $this->failed;
self::$allFailures = array_merge(self::$allFailures, $this->failures);
return $this->failures;
}
public static function globalSummary(): array
{
return [
'passed' => self::$totalPassed,
'failed' => self::$totalFailed,
'failures' => self::$allFailures,
];
}
}