Nette Documentation Preview

syntax
Assertions
**********

.[perex]
Assertions dienen dazu, zu bestätigen, dass der tatsächliche Wert dem erwarteten Wert entspricht. Es sind Methoden der Klasse `Tester\Assert`.

Wählen Sie die jeweils passendste Assertion. `Assert::same($a, $b)` ist besser als `Assert::true($a === $b)`, weil sie bei einem Fehlschlag eine aussagekräftige Fehlermeldung anzeigt. Im zweiten Fall erhalten wir nur `false should be true`, was uns nichts über den Inhalt der Variablen `$a` und `$b` sagt.

Die meisten Assertions können außerdem eine optionale Beschreibung im Parameter `$description` haben, die in der Fehlermeldung angezeigt wird, wenn die Erwartung fehlschlägt.

Die Beispiele setzen voraus, dass folgender Alias angelegt wurde:

```php
use Tester\Assert;
```


Assert::same($expected, $actual, ?string $description=null) .[method]
---------------------------------------------------------------------
`$expected` muss identisch mit `$actual` sein. Das entspricht dem PHP-Operator `===`.


Assert::notSame($expected, $actual, ?string $description=null) .[method]
------------------------------------------------------------------------
Gegenteil von `Assert::same()`, entspricht also dem PHP-Operator `!==`.


Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false) .[method]
-------------------------------------------------------------------------------------------------------------------------
`$expected` muss gleich `$actual` sein. Anders als bei `Assert::same()` werden die Identität von Objekten, die Reihenfolge der Schlüssel-Wert-Paare in Arrays und geringfügig abweichende Dezimalzahlen ignoriert, was sich über `$matchIdentity` und `$matchOrder` ändern lässt.

Die folgenden Fälle sind aus Sicht von `equal()` gleich, aus Sicht von `same()` aber nicht:

```php
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
	['first' => 11, 'second' => 22],
	['second' => 22, 'first' => 11],
);
```

Aber Achtung, die Arrays `[1, 2]` und `[2, 1]` sind nicht gleich, denn hier unterscheidet sich nur die Reihenfolge der Werte, nicht die der Schlüssel-Wert-Paare. Das Array `[1, 2]` lässt sich auch als `[0 => 1, 1 => 2]` schreiben, und `[1 => 2, 0 => 1]` wird deshalb als gleich betrachtet.

In `$expected` können Sie außerdem sogenannte [#Expectations] verwenden.


Assert::notEqual($expected, $actual, ?string $description=null) .[method]
-------------------------------------------------------------------------
Gegenteil von `Assert::equal()`.


Assert::contains($needle, string|array $actual, ?string $description=null) .[method]
------------------------------------------------------------------------------------
Ist `$actual` ein String, muss er den Teilstring `$needle` enthalten. Ist es ein Array, muss es das Element `$needle` enthalten (streng verglichen).


Assert::notContains($needle, string|array $actual, ?string $description=null) .[method]
---------------------------------------------------------------------------------------
Gegenteil von `Assert::contains()`.


Assert::hasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4.0}
----------------------------------------------------------------------------------------------------------
`$actual` muss ein Array sein und den Schlüssel `$needle` enthalten.


Assert::hasNotKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4.0}
-------------------------------------------------------------------------------------------------------------
`$actual` muss ein Array sein und darf den Schlüssel `$needle` nicht enthalten.


Assert::true($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` muss `true` sein, also `$value === true`.


Assert::truthy($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` muss truthy sein, also die Bedingung `if ($value) ...` erfüllen.


Assert::false($value, ?string $description=null) .[method]
----------------------------------------------------------
`$value` muss `false` sein, also `$value === false`.


Assert::falsey($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` muss falsey sein, also die Bedingung `if (!$value) ...` erfüllen.


Assert::null($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` muss `null` sein, also `$value === null`.


Assert::notNull($value, ?string $description=null) .[method]
------------------------------------------------------------
`$value` darf nicht `null` sein, also `$value !== null`.


Assert::nan($value, ?string $description=null) .[method]
--------------------------------------------------------
`$value` muss Not a Number sein. Zum Testen von NAN-Werten verwenden Sie ausschließlich `Assert::nan()`. Der Wert NAN ist sehr speziell, und Assertions wie `Assert::same()` oder `Assert::equal()` können sich unerwartet verhalten.


Assert::count($count, Countable|array $value, ?string $description=null) .[method]
----------------------------------------------------------------------------------
Die Anzahl der Elemente in `$value` muss `$count` sein. Das entspricht `count($value) === $count`.


Assert::type(string|object $type, $value, ?string $description=null) .[method]
------------------------------------------------------------------------------
`$value` muss vom angegebenen Typ sein. Als `$type` können wir einen String verwenden:
- `array`
- `list` - ein Array, das nach einer aufsteigenden Reihe numerischer Schlüssel ab null indiziert ist
- `bool`
- `callable`
- `float`
- `int`
- `null`
- `object`
- `resource`
- `scalar`
- `string`
- einen Klassennamen oder direkt ein Objekt, dann muss `$value instanceof $type` gelten


Assert::exception(callable $callable, string $class, ?string $message=null, $code=null) .[method]
-------------------------------------------------------------------------------------------------
Beim Aufruf von `$callable` muss eine Exception der Klasse `$class` geworfen werden. Geben wir `$message` an, muss auch die Nachricht der Exception [zum Muster passen |#Assert::match()]. Und geben wir `$code` an, müssen auch die Codes streng übereinstimmen.

Der folgende Test schlägt zum Beispiel fehl, weil die Nachricht der Exception nicht passt:

```php
Assert::exception(
	fn() => throw new App\InvalidValueException('Zero value'),
	App\InvalidValueException::class,
	'Value is too low',
);
```

`Assert::exception()` gibt die geworfene Exception zurück, sodass Sie auch eine verschachtelte Exception testen können.

```php
$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(string $callable, int|string|array $type, ?string $message=null) .[method]
----------------------------------------------------------------------------------------
Prüft, ob die Funktion `$callable` die erwarteten Fehler erzeugt hat (also Warnings, Notices usw.). Geben Sie als `$type` eine der `E_...`-Konstanten an, zum Beispiel `E_WARNING`. Und geben wir `$message` an, muss auch die Fehlermeldung [zum Muster passen |#Assert::match()]. Zum Beispiel:

```php
Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Undefined variable: i',
);
```

Erzeugt der Callback mehrere Fehler, müssen wir sie alle in der exakten Reihenfolge erwarten. Übergeben Sie in diesem Fall ein Array in `$type`:

```php
Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Undefined variable: a'],
	[E_NOTICE, 'Undefined variable: b'],
]);
```

.[note]
Wenn Sie als `$type` einen Klassennamen angeben, verhält sie sich genauso wie `Assert::exception()`.


Assert::noError(callable $callable) .[method]
---------------------------------------------
Prüft, ob die Funktion `$callable` keine Warnung, keinen Fehler und keine Exception erzeugt hat. Das ist nützlich, um Codeabschnitte zu testen, in denen es keine andere Assertion gibt.


Assert::match(string $pattern, $actual, ?string $description=null) .[method]
----------------------------------------------------------------------------
`$actual` muss zum Muster `$pattern` passen. Wir können zwei Varianten von Mustern verwenden: reguläre Ausdrücke oder Wildcards.

Übergeben wir als `$pattern` einen regulären Ausdruck, müssen wir `~` oder `#` als Begrenzer verwenden. Andere Begrenzer werden nicht unterstützt. Zum Beispiel ein Test, bei dem `$var` nur hexadezimale Ziffern enthalten darf:

```php
Assert::match('#^[0-9a-f]+$#i', $var);
```

Die zweite Variante ähnelt dem Vergleich gewöhnlicher Strings, aber wir können in `$pattern` verschiedene Wildcards verwenden:

- `%a%` ein oder mehr beliebige Zeichen außer Zeilenendezeichen
- `%a?%` null oder mehr beliebige Zeichen außer Zeilenendezeichen
- `%A%` ein oder mehr beliebige Zeichen einschließlich Zeilenendezeichen
- `%A?%` null oder mehr beliebige Zeichen einschließlich Zeilenendezeichen
- `%s%` ein oder mehr Whitespace-Zeichen außer Zeilenendezeichen
- `%s?%` null oder mehr Whitespace-Zeichen außer Zeilenendezeichen
- `%S%` ein oder mehr Zeichen außer Whitespace-Zeichen
- `%S?%` null oder mehr Zeichen außer Whitespace-Zeichen
- `%c%` ein einzelnes beliebiges Zeichen (außer Zeilenende)
- `%d%` eine oder mehr Ziffern
- `%d?%` null oder mehr Ziffern
- `%i%` ganzzahliger Wert mit Vorzeichen
- `%f%` Gleitkommazahl
- `%h%` eine oder mehr hexadezimale Ziffern
- `%w%` ein oder mehr alphanumerische Zeichen
- `%ds%` Verzeichnistrenner (`/` oder `\`)
- `%%` ein Prozentzeichen

Beispiele:

```php
# Wieder ein Test auf eine hexadezimale Zahl
Assert::match('%h%', $var);

# Verallgemeinerung von Dateipfad und Zeilennummer
Assert::match('Error in file %a% on line %i%', $errorMessage);
```


Assert::notMatch(string $pattern, $actual, ?string $description=null) .[method]{data-version:2.5.6}
---------------------------------------------------------------------------------------------------
Gegenteil von `Assert::match()`.


Assert::matchFile(string $file, $actual, ?string $description=null) .[method]
-----------------------------------------------------------------------------
Diese Assertion ist identisch mit [#Assert::match()], aber das Muster wird aus der Datei `$file` geladen. Das ist beim Testen sehr langer Strings nützlich. Die Testdatei bleibt übersichtlich.


Assert::fail(string $message, $actual=null, $expected=null) .[method]
---------------------------------------------------------------------
Diese Assertion schlägt immer fehl. Manchmal ist das einfach nützlich. Optional können wir den erwarteten und den tatsächlichen Wert angeben.


Expectations
------------
Wenn wir komplexere Strukturen mit nicht konstanten Elementen vergleichen wollen, reichen die oben genannten Assertions möglicherweise nicht aus. Nehmen wir zum Beispiel an, wir testen eine Methode, die einen neuen Benutzer anlegt und dessen Attribute als Array zurückgibt. Den Wert des Passwort-Hashes kennen wir nicht, wir wissen aber, dass es ein hexadezimaler String sein muss. Und vom nächsten Element wissen wir nur, dass es ein Objekt `DateTime` sein muss.

In solchen Situationen können wir im Parameter `$expected` der Methoden `Assert::equal()` und `Assert::notEqual()` `Tester\Expect` verwenden, womit sich die Struktur leicht beschreiben lässt.

```php
use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # wir erwarten eine Ganzzahl
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # wir erwarten einen String, der zum Muster passt
	'created_at' => Expect::type(DateTime::class), # wir erwarten eine Instanz der Klasse
], User::create(123, 'milo', 'RandomPaSsWoRd'));
```

Mit `Expect` können wir fast dieselben Assertions durchführen wie mit `Assert`. Es stehen uns also die Methoden `Expect::same()`, `Expect::match()`, `Expect::count()` usw. zur Verfügung. Außerdem können wir sie verketten:

```php
Expect::type(MyIterator::class)->andCount(5);  # wir erwarten MyIterator und die Anzahl der Elemente ist 5
```

Alternativ können wir eigene Handler für Assertions schreiben.

```php
Expect::that(function ($value) {
	# false zurückgeben, wenn die Erwartung fehlschlägt
});
```


Untersuchung fehlgeschlagener Assertions
----------------------------------------
Wenn eine Assertion fehlschlägt, gibt Tester aus, worin der Fehler besteht. Vergleichen wir komplexe Strukturen, erzeugt Tester Dumps der verglichenen Werte und speichert sie im Verzeichnis `output`. Schlägt zum Beispiel der fiktive Test `Arrays.recursive.phpt` fehl, werden die Dumps folgendermaßen abgelegt:

```
app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # tatsächlicher Wert
	│   └── Arrays.recursive.expected  # erwarteter Wert
	│
	└── Arrays.recursive.phpt          # fehlgeschlagener Test
```

Den Namen des Verzeichnisses können wir über `Tester\Dumper::$dumpDir` ändern.

Assertions

Assertions dienen dazu, zu bestätigen, dass der tatsächliche Wert dem erwarteten Wert entspricht. Es sind Methoden der Klasse Tester\Assert.

Wählen Sie die jeweils passendste Assertion. Assert::same($a, $b) ist besser als Assert::true($a === $b), weil sie bei einem Fehlschlag eine aussagekräftige Fehlermeldung anzeigt. Im zweiten Fall erhalten wir nur false should be true, was uns nichts über den Inhalt der Variablen $a und $b sagt.

Die meisten Assertions können außerdem eine optionale Beschreibung im Parameter $description haben, die in der Fehlermeldung angezeigt wird, wenn die Erwartung fehlschlägt.

Die Beispiele setzen voraus, dass folgender Alias angelegt wurde:

use Tester\Assert;

Assert::same($expected, $actual, ?string $description=null)

$expected muss identisch mit $actual sein. Das entspricht dem PHP-Operator ===.

Assert::notSame($expected, $actual, ?string $description=null)

Gegenteil von Assert::same(), entspricht also dem PHP-Operator !==.

Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)

$expected muss gleich $actual sein. Anders als bei Assert::same() werden die Identität von Objekten, die Reihenfolge der Schlüssel-Wert-Paare in Arrays und geringfügig abweichende Dezimalzahlen ignoriert, was sich über $matchIdentity und $matchOrder ändern lässt.

Die folgenden Fälle sind aus Sicht von equal() gleich, aus Sicht von same() aber nicht:

Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
	['first' => 11, 'second' => 22],
	['second' => 22, 'first' => 11],
);

Aber Achtung, die Arrays [1, 2] und [2, 1] sind nicht gleich, denn hier unterscheidet sich nur die Reihenfolge der Werte, nicht die der Schlüssel-Wert-Paare. Das Array [1, 2] lässt sich auch als [0 => 1, 1 => 2] schreiben, und [1 => 2, 0 => 1] wird deshalb als gleich betrachtet.

In $expected können Sie außerdem sogenannte Expectations verwenden.

Assert::notEqual($expected, $actual, ?string $description=null)

Gegenteil von Assert::equal().

Assert::contains($needle, string|array $actual, ?string $description=null)

Ist $actual ein String, muss er den Teilstring $needle enthalten. Ist es ein Array, muss es das Element $needle enthalten (streng verglichen).

Assert::notContains($needle, string|array $actual, ?string $description=null)

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::hasNotKey(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 truthy 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 falsey 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 Wert NAN ist sehr speziell, und Assertions wie Assert::same() oder Assert::equal() können sich unerwartet verhalten.

Assert::count($count, Countable|array $value, ?string $description=null)

Die Anzahl der Elemente in $value muss $count sein. Das entspricht count($value) === $count.

Assert::type(string|object $type, $value, ?string $description=null)

$value muss vom angegebenen Typ sein. Als $type können wir einen String verwenden:

  • array
  • list – ein Array, das nach einer aufsteigenden Reihe numerischer Schlüssel ab null indiziert ist
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • einen Klassennamen oder direkt ein Objekt, dann muss $value instanceof $type gelten

Assert::exception(callable $callable, string $class, ?string $message=null, $code=null)

Beim Aufruf von $callable muss eine Exception der Klasse $class geworfen werden. Geben wir $message an, muss auch die Nachricht der Exception zum Muster passen. Und geben wir $code an, müssen auch die Codes streng übereinstimmen.

Der folgende Test schlägt zum Beispiel fehl, weil die Nachricht der Exception nicht passt:

Assert::exception(
	fn() => throw new App\InvalidValueException('Zero value'),
	App\InvalidValueException::class,
	'Value is too low',
);

Assert::exception() gibt die geworfene Exception zurück, sodass Sie auch eine verschachtelte Exception testen können.

$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(string $callable, int|string|array $type, ?string $message=null)

Prüft, ob die Funktion $callable die erwarteten Fehler erzeugt hat (also Warnings, Notices usw.). Geben Sie als $type eine der E_...-Konstanten an, zum Beispiel E_WARNING. Und geben wir $message an, muss auch die Fehlermeldung zum Muster passen. Zum Beispiel:

Assert::error(
	fn() => $i++,
	E_NOTICE,
	'Undefined variable: i',
);

Erzeugt der Callback mehrere Fehler, müssen wir sie alle in der exakten Reihenfolge erwarten. Übergeben Sie in diesem Fall ein Array in $type:

Assert::error(function () {
	$a++;
	$b++;
}, [
	[E_NOTICE, 'Undefined variable: a'],
	[E_NOTICE, 'Undefined variable: b'],
]);

Wenn Sie als $type einen Klassennamen angeben, verhält sie sich genauso wie Assert::exception().

Assert::noError(callable $callable)

Prüft, ob die Funktion $callable keine Warnung, keinen Fehler und keine Exception erzeugt hat. Das ist nützlich, um Codeabschnitte zu testen, in denen es keine andere Assertion gibt.

Assert::match(string $pattern, $actual, ?string $description=null)

$actual muss zum Muster $pattern passen. Wir können zwei Varianten von Mustern verwenden: reguläre Ausdrücke oder Wildcards.

Übergeben wir als $pattern einen regulären Ausdruck, müssen wir ~ oder # als Begrenzer verwenden. Andere Begrenzer 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 Vergleich gewöhnlicher Strings, aber wir können in $pattern verschiedene Wildcards verwenden:

  • %a% ein oder mehr beliebige Zeichen außer Zeilenendezeichen
  • %a?% null oder mehr beliebige Zeichen außer Zeilenendezeichen
  • %A% ein oder mehr beliebige Zeichen einschließlich Zeilenendezeichen
  • %A?% null oder mehr beliebige Zeichen einschließlich Zeilenendezeichen
  • %s% ein oder mehr Whitespace-Zeichen außer Zeilenendezeichen
  • %s?% null oder mehr Whitespace-Zeichen außer Zeilenendezeichen
  • %S% ein oder mehr Zeichen außer Whitespace-Zeichen
  • %S?% null oder mehr Zeichen außer Whitespace-Zeichen
  • %c% ein einzelnes beliebiges Zeichen (außer Zeilenende)
  • %d% eine oder mehr Ziffern
  • %d?% null oder mehr Ziffern
  • %i% ganzzahliger Wert mit Vorzeichen
  • %f% Gleitkommazahl
  • %h% eine oder mehr hexadezimale Ziffern
  • %w% ein oder mehr alphanumerische Zeichen
  • %ds% Verzeichnistrenner (/ oder \)
  • %% ein Prozentzeichen

Beispiele:

# Wieder ein Test auf eine hexadezimale Zahl
Assert::match('%h%', $var);

# Verallgemeinerung von Dateipfad und Zeilennummer
Assert::match('Error in file %a% on line %i%', $errorMessage);

Assert::notMatch(string $pattern, $actual, ?string $description=null)

Gegenteil von Assert::match().

Assert::matchFile(string $file, $actual, ?string $description=null)

Diese Assertion ist identisch mit Assert::match(), aber das Muster wird aus der Datei $file geladen. Das ist beim Testen sehr langer Strings nützlich. 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 den erwarteten und den tatsächlichen Wert angeben.

Expectations

Wenn wir komplexere Strukturen mit nicht konstanten Elementen vergleichen wollen, reichen die oben genannten Assertions möglicherweise nicht aus. Nehmen wir zum Beispiel an, wir testen eine Methode, die einen neuen Benutzer anlegt und dessen Attribute als Array zurückgibt. Den Wert des Passwort-Hashes kennen wir nicht, wir wissen aber, dass es ein hexadezimaler String sein muss. Und vom nächsten Element wissen wir nur, dass es ein Objekt DateTime sein muss.

In solchen Situationen können wir im Parameter $expected der Methoden Assert::equal() und Assert::notEqual() Tester\Expect verwenden, womit sich die Struktur leicht beschreiben lässt.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # wir erwarten eine Ganzzahl
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # wir erwarten einen String, der zum Muster passt
	'created_at' => Expect::type(DateTime::class), # wir erwarten eine Instanz der Klasse
], User::create(123, 'milo', 'RandomPaSsWoRd'));

Mit Expect können wir fast dieselben Assertions durchführen wie mit Assert. Es stehen uns also die Methoden 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 ist 5

Alternativ können wir eigene Handler für Assertions schreiben.

Expect::that(function ($value) {
	# false zurückgeben, wenn die Erwartung fehlschlägt
});

Untersuchung fehlgeschlagener Assertions

Wenn eine Assertion fehlschlägt, gibt Tester aus, worin der Fehler besteht. Vergleichen wir komplexe Strukturen, erzeugt Tester Dumps der verglichenen Werte und speichert sie im Verzeichnis output. Schlägt zum Beispiel der fiktive Test Arrays.recursive.phpt fehl, werden die Dumps folgendermaßen abgelegt:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # tatsächlicher Wert
	│   └── Arrays.recursive.expected  # erwarteter Wert
	│
	└── Arrays.recursive.phpt          # fehlgeschlagener Test

Den Namen des Verzeichnisses können wir über Tester\Dumper::$dumpDir ändern.