Assertions (Zusicherungen)
Assertions werden verwendet, um zu bestätigen, dass ein tatsächlicher Wert einem erwarteten Wert entspricht.
Dies sind Methoden der Klasse Tester\Assert
.
Wählen Sie die am besten geeigneten Assertions. Es ist besser, Assert::same($a, $b)
zu verwenden als
Assert::true($a === $b)
, da im Fehlerfall eine aussagekräftige Fehlermeldung angezeigt wird. Im zweiten Fall nur
false should be true
, was uns nichts über den Inhalt der Variablen $a
und $b
sagt.
Die meisten Assertions können auch eine optionale Beschreibung im Parameter $description
haben, die in der
Fehlermeldung angezeigt wird, wenn die Erwartung fehlschlägt.
Die Beispiele setzen voraus, dass ein Alias erstellt wurde:
use Tester\Assert;
Assert::same ($expected, $actual, ?string $description=null)
$expected
muss identisch mit $actual
sein. Dasselbe wie der PHP-Operator ===
.
Assert::notSame ($expected, $actual, ?string $description=null)
Das Gegenteil von Assert::same()
, also dasselbe wie der PHP-Operator !==
.
Assert::equal ($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected
muss gleich $actual
sein. Im Gegensatz zu Assert::same()
werden die Identität
von Objekten, die Reihenfolge von Schlüssel-Wert-Paaren in Arrays und geringfügig unterschiedliche Dezimalzahlen ignoriert, was
durch Setzen von $matchIdentity
und $matchOrder
geändert werden kann.
Die folgenden Fälle sind aus Sicht von equal()
gleich, aber nicht aus Sicht von same()
:
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['first' => 11, 'second' => 22],
['second' => 22, 'first' => 11],
);
Aber Vorsicht, die Arrays [1, 2]
und [2, 1]
sind nicht gleich, da sie sich nur in der Reihenfolge der
Werte unterscheiden, nicht der Schlüssel-Wert-Paare. Das Array [1, 2]
kann auch als
[0 => 1, 1 => 2]
geschrieben werden, daher wird [1 => 2, 0 => 1]
als gleich betrachtet.
Weiterhin kann in $expected
die sogenannte Erwartung (Expectation) verwendet
werden.
Assert::notEqual ($expected, $actual, ?string $description=null)
Das Gegenteil von Assert::equal()
.
Assert::contains ($needle, string|array $actual, ?string $description=null)
Wenn $actual
eine Zeichenkette ist, muss sie die Teilzeichenkette $needle
enthalten. Wenn es ein
Array ist, muss es das Element $needle
enthalten (strikt verglichen mit ===
).
Assert::notContains ($needle, string|array $actual, ?string $description=null)
Das Gegenteil von Assert::contains()
.
Assert::hasKey (string|int $needle, array $actual, ?string $description=null)
$actual
muss ein Array sein und den Schlüssel $needle
enthalten.
Assert::notHasKey (string|int $needle, array $actual, ?string $description=null)
$actual
muss ein Array sein und darf den Schlüssel $needle
nicht enthalten.
Assert::true ($value, ?string $description=null)
$value
muss true
sein, also $value === true
.
Assert::truthy ($value, ?string $description=null)
$value
muss wahrheitsgemäß sein, also die Bedingung if ($value) ...
erfüllen.
Assert::false ($value, ?string $description=null)
$value
muss false
sein, also $value === false
.
Assert::falsey ($value, ?string $description=null)
$value
muss falsch sein, also die Bedingung if (!$value) ...
erfüllen.
Assert::null ($value, ?string $description=null)
$value
muss null
sein, also $value === null
.
Assert::notNull ($value, ?string $description=null)
$value
darf nicht null
sein, also $value !== null
.
Assert::nan ($value, ?string $description=null)
$value
muss Not a Number sein. Zum Testen von NAN-Werten verwenden Sie ausschließlich Assert::nan()
.
Der NAN-Wert ist sehr spezifisch und die Assertions Assert::same()
oder Assert::equal()
können
unerwartet funktionieren.
Assert::count ($count, Countable|array $value, ?string $description=null)
Die Anzahl der Elemente in $value
muss $count
sein. Also dasselbe wie
count($value) === $count
.
Assert::type (string|object $type, $value, ?string $description=null)
$value
muss vom angegebenen Typ sein. Als $type
können wir eine Zeichenkette verwenden:
array
list
– Array, das nach einer aufsteigenden Reihe numerischer Schlüssel ab Null indiziert istbool
callable
float
int
null
object
resource
scalar
string
- Klassenname oder direkt Objekt, dann muss
$value instanceof $type
sein
Assert::exception (callable $callable, string $class, ?string $message=null, $code=null)
Beim Aufruf von $callable
muss eine Ausnahme der Klasse $class
geworfen werden. Wenn wir
$message
angeben, muss auch die Ausnahmemeldung dem Muster entsprechen, und wenn wir
$code
angeben, müssen auch die Codes strikt übereinstimmen (===
).
Der folgende Test schlägt fehl, da die Ausnahmemeldung nicht übereinstimmt:
Assert::exception(
fn() => throw new App\InvalidValueException('Zero value'),
App\InvalidValueException::class,
'Value is too low', // Erwartete Nachricht stimmt nicht überein
);
Assert::exception()
gibt die geworfene Ausnahme zurück, sodass auch eine verschachtelte Ausnahme getestet
werden kann.
$e = Assert::exception(
fn() => throw new MyException('Something is wrong', 0, new RuntimeException),
MyException::class,
'Something is wrong',
);
Assert::type(RuntimeException::class, $e->getPrevious());
Assert::error (callable $callable, int|string|array $type, ?string $message=null)
Überprüft, ob die Funktion $callable
die erwarteten PHP-Fehler (d. h. Warnungen, Hinweise usw.) generiert hat.
Als $type
geben wir eine der E_...
-Konstanten an, z. B. E_WARNING
. Und wenn wir
$message
angeben, muss auch die Fehlermeldung dem Muster entsprechen. Zum
Beispiel:
Assert::error(
fn() => $i++,
E_NOTICE,
'Undefined variable: i',
);
Wenn der Callback mehrere Fehler generiert, müssen wir sie alle in der genauen Reihenfolge erwarten. In diesem Fall übergeben
wir in $type
ein Array:
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Undefined variable: a'],
[E_NOTICE, 'Undefined variable: b'],
]);
Wenn Sie als $type
einen Klassennamen angeben, verhält es sich wie
Assert::exception()
.
Assert::noError (callable $callable)
Überprüft, ob die Funktion $callable
keine PHP-Warnung, keinen Fehler oder keine Ausnahme generiert hat. Eignet
sich zum Testen von Codeabschnitten, in denen keine weitere Assertion vorhanden ist.
Assert::match (string $pattern, $actual, ?string $description=null)
$actual
muss dem Muster $pattern
entsprechen. Wir können zwei Varianten von Mustern verwenden:
reguläre Ausdrücke oder Platzhalter.
Wenn wir als $pattern
einen regulären Ausdruck übergeben, müssen wir ihn mit ~
oder #
begrenzen, andere Trennzeichen werden nicht unterstützt. Zum Beispiel ein Test, bei dem $var
nur hexadezimale
Ziffern enthalten darf:
Assert::match('#^[0-9a-f]$#i', $var);
Die zweite Variante ähnelt dem normalen Zeichenkettenvergleich, aber in $pattern
können wir verschiedene
Platzhalter verwenden:
%a%
ein oder mehrere Zeichen, außer Zeilenendezeichen%a?%
kein oder mehrere Zeichen, außer Zeilenendezeichen%A%
ein oder mehrere Zeichen, einschließlich Zeilenendezeichen%A?%
kein oder mehrere Zeichen, einschließlich Zeilenendezeichen%s%
ein oder mehrere Leerzeichen, außer Zeilenendezeichen%s?%
kein oder mehrere Leerzeichen, außer Zeilenendezeichen%S%
ein oder mehrere Zeichen, außer Leerzeichen%S?%
kein oder mehrere Zeichen, außer Leerzeichen%c%
jedes einzelne Zeichen, außer dem Zeilenendezeichen%d%
eine oder mehrere Ziffern%d?%
keine oder mehrere Ziffern%i%
vorzeichenbehafteter ganzzahliger Wert%f%
Fließkommazahl%h%
eine oder mehrere hexadezimale Ziffern%w%
ein oder mehrere alphanumerische Zeichen%%
Zeichen %
Beispiele:
# Erneut Test auf hexadezimale Zahl
Assert::match('%h%', $var);
# Verallgemeinerung des Dateipfads und der Zeilennummer
Assert::match('Error in file %a% on line %i%', $errorMessage);
Assert::matchFile (string $file, $actual, ?string $description=null)
Die Assertion ist identisch mit Assert::match(), aber das Muster wird aus der Datei
$file
geladen. Dies ist nützlich zum Testen sehr langer Zeichenketten. Die Testdatei bleibt übersichtlich.
Assert::fail (string $message, $actual=null, $expected=null)
Diese Assertion schlägt immer fehl. Manchmal ist das einfach nützlich. Optional können wir auch den erwarteten und den tatsächlichen Wert angeben.
Erwartungen
Wenn wir komplexere Strukturen mit nicht-konstanten Elementen vergleichen möchten, reichen die oben genannten Assertions
möglicherweise nicht aus. Zum Beispiel testen wir eine Methode, die einen neuen Benutzer erstellt und seine Attribute als Array
zurückgibt. Den Wert des Passwort-Hashes kennen wir nicht, aber wir wissen, dass es eine hexadezimale Zeichenkette sein muss. Und
über ein weiteres Element wissen wir nur, dass es ein DateTime
-Objekt sein muss.
In diesen Situationen können wir Tester\Expect
innerhalb des $expected
-Parameters der Methoden
Assert::equal()
und Assert::notEqual()
verwenden, mit denen sich die Struktur leicht
beschreiben lässt.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # wir erwarten eine ganze Zahl
'username' => 'milo',
'password' => Expect::match('%h%'), # wir erwarten eine Zeichenkette, die dem Muster entspricht
'created_at' => Expect::type(DateTime::class), # wir erwarten eine Instanz der Klasse
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Mit Expect
können wir fast die gleichen Assertions wie mit Assert
durchführen. Das heißt, uns
stehen Methoden wie Expect::same()
, Expect::match()
, Expect::count()
usw. zur Verfügung.
Außerdem können wir sie verketten:
Expect::type(MyIterator::class)->andCount(5); # wir erwarten MyIterator und die Anzahl der Elemente 5
Oder wir können eigene Assertion-Handler schreiben.
Expect::that(function ($value) {
# geben false zurück, wenn die Erwartung fehlschlägt
});
Untersuchung fehlgeschlagener Assertions
Wenn eine Assertion fehlschlägt, gibt Tester aus, worin der Fehler besteht. Wenn wir komplexere Strukturen vergleichen,
erstellt Tester Dumps der verglichenen Werte und speichert sie im Verzeichnis output
. Zum Beispiel werden bei
Fehlschlagen des fiktiven Tests Arrays.recursive.phpt
die Dumps wie folgt gespeichert:
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # tatsächlicher Wert
│ └── Arrays.recursive.expected # erwarteter Wert
│
└── Arrays.recursive.phpt # fehlgeschlagener Test
Den Verzeichnisnamen können wir über Tester\Dumper::$dumpDir
ändern.