Nette Documentation Preview

syntax
Искове
******

.[perex]
Твърденията се използват за потвърждаване, че действителната стойност съответства на очакваната стойност. Това са методи `Tester\Assert`.

Изберете най-точните твърдения. По-добре `Assert::same($a, $b)`, отколкото `Assert::true($a === $b)`, защото при неуспех извежда смислено съобщение за грешка. Във втория случай получаваме само `false should be true`, а той не казва нищо за съдържанието на променливите $a и $b.

Повечето оператори могат да имат и незадължителен `$description`, който се появява в съобщението за грешка, ако не успеят.

Примерите предполагат, че е дефиниран следният псевдоним на клас:

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


Assert::same($expected, $actual, ?string $description=null) .[method]
---------------------------------------------------------------------
`$expected` трябва да бъде същото като `$actual`. Това е същото като оператора на PHP `===`.


Assert::notSame($expected, $actual, ?string $description=null) .[method]
------------------------------------------------------------------------
Противоположен на `Assert::same()`, така че е същият като оператора на PHP `!==`.


Assert::equal($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false) .[method]
-------------------------------------------------------------------------------------------------------------------------
`$expected` трябва да бъде същото като `$actual`. За разлика от `Assert::same()`, идентичността на обектите, редът на двойките ключ => стойност в масивите и леко различаващите се десетични числа се игнорират, което може да се промени чрез задаване на `$matchIdentity` и `$matchOrder`.

Следните случаи са идентични за `equal()`, но не и за `same()`:

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

Предупреждаваме ви обаче, че масивът `[1, 2]` и `[2, 1]` не са еднакви, тъй като се различава само редът на стойностите, а не двойките ключ => стойност. Масивът `[1, 2]` може да се запише и като `[0 => 1, 1 => 2]` и следователно `[1 => 2, 0 => 1]` ще се считат за равни.

Можете да използвате и т.нар. [изчакване |#Expectations] в `$expected`.


Assert::notEqual($expected, $actual, ?string $description=null) .[method]
-------------------------------------------------------------------------
Контраст `Assert::equal()`.


Assert::contains($needle, string|array $actual, ?string $description=null) .[method]
------------------------------------------------------------------------------------
Ако `$actual` е низ, той трябва да съдържа подниз от `$needle`. Ако е масив, той трябва да съдържа елемент `$needle` (сравнява се стриктно).


Assert::notContains($needle, string|array $actual, ?string $description=null) .[method]
---------------------------------------------------------------------------------------
Обратното на `Assert::contains()`.


Assert::hasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4}
--------------------------------------------------------------------------------------------------------
`$actual` трябва да бъде масив и да съдържа ключа `$needle`.


Assert::notHasKey(string|int $needle, array $actual, ?string $description=null) .[method]{data-version:2.4}
-----------------------------------------------------------------------------------------------------------
`$actual` трябва да бъде масив и да не съдържа ключа `$needle`.


Assert::true($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` трябва да бъде `true`, така че `$value === true`.


Assert::truthy($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` трябва да е вярно, така че то отговаря на условието `if ($value) ...`.


Assert::false($value, ?string $description=null) .[method]
----------------------------------------------------------
`$value` трябва да бъде `false`, следователно `$value === false`.


Assert::falsey($value, ?string $description=null) .[method]
-----------------------------------------------------------
`$value` трябва да е false, така че то отговаря на условието `if (!$value) ...`.


Assert::null($value, ?string $description=null) .[method]
---------------------------------------------------------
`$value` трябва да бъде `null`, така че `$value === null`.


Assert::notNull($value, ?string $description=null) .[method]
------------------------------------------------------------
`$value` не следва да бъде `null`, следователно `$value !== null`.


Assert::nan($value, ?string $description=null) .[method]
--------------------------------------------------------
`$value` трябва да бъде Не е число. Използвайте `Assert::nan()` само за тестване на NAN. Стойността на NAN е много специфична и изявленията `Assert::same()` или `Assert::equal()` могат да се държат непредсказуемо.


Assert::count($count, Countable|array $value, ?string $description=null) .[method]
----------------------------------------------------------------------------------
Броят на елементите в `$value` трябва да е равен на `$count`. Това е същото като `count($value) === $count`.


Assert::type(string|object $type, $value, ?string $description=null) .[method]
------------------------------------------------------------------------------
`$value` трябва да е от посочения тип. Като `$type` можем да използваме символа:
- `array`
- `list` е масив, индексиран във възходящ цифров ред на ключовете от нула.
- `bool`
- `callable`
- `float`
- `int`
- `null`
- `object`
- `resource`
- `scalar`
- `string`
- директно име на клас или обект, тогава трябва да предадете `$value instanceof $type`


Assert::exception(callable $callable, string $class, ?string $message=null, $code=null) .[method]
-------------------------------------------------------------------------------------------------
При извикване на `$callable` трябва да се хвърли изключение за `$class`. Ако подадем `$message`, съобщението за изключение трябва да [съвпадне |#Assert-match]. И ако подадем `$code`, кодът на изключението трябва да е същият.

Например този тест не успява, защото съобщението за изключение не съвпада:

```php
Assert::exception(
	fn() => throw new App\InvalidValueException('Нулевое значение'),
	App\InvalidValueException::class,
	'Значение слишком мало',
);
```

`Assert::exception()` връща хвърлено изключение, за да можете да проверите вложеното изключение.

```php
$e = Assert::exception(
	fn() => throw new MyException('Что-то не так', 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]
----------------------------------------------------------------------------------------
Проверява дали извикването на `$callable` генерира очакваните грешки (т.е. предупреждения, известия и т.н.). Като `$type` посочваме една от константите `E_...`, например `E_WARNING`. А ако подадем `$message`, съобщението за грешка също трябва да [съответства на |#Assert-match] шаблона. Например:

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

Ако обратното извикване генерира повече грешки, трябва да ги очакваме в точен ред. В този случай предаваме масива на `$type`:

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

.[note]
Ако `$type` е име на клас, това изявление се държи по същия начин като `Assert::exception()`.


Assert::noError(callable $callable) .[method]
---------------------------------------------
Проверява дали `$callable` не изхвърля никакви предупреждения/бележки/грешки или изключения на PHP. Това е полезно за проверка на части от кода, в които няма други оператори.


Assert::match(string $pattern, $actual, ?string $description=null) .[method]
----------------------------------------------------------------------------
`$actual` трябва да съвпада с `$pattern`. Можем да използваме две опции за шаблони: регулярни изрази или заместващи символи.

Ако подадем регулярен израз като `$pattern`, трябва да използваме `~` or `#`, за да го отделим. Други разделители не се поддържат. Например тест, в който `$var` трябва да съдържа само шестнадесетични цифри:

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

Друга възможност е подобна на сравнението на низове, но можем да използваме някои заместващи символи в `$pattern`:

- `%a%` един или повече символи, с изключение на символите за край на реда
- `%a?%` нула или повече символи, с изключение на символите за край на реда
- `%A%` един или повече от всички символи, включително символите за край на реда
- `%A?%` нула или повече символа, включително символите за край на реда
- `%s%` един или повече интервали, с изключение на символите за край на реда
- `%s?%` нула или повече интервали, с изключение на символите за край на реда
- `%S%` един или повече знаци, с изключение на интервал
- `%S?%` нула или повече знаци, с изключение на интервал
- `%c%` една или повече цифри от всякакъв вид (с изключение на края на реда)
- `%d%` една или повече цифри
- `%d?%` нула или повече цифри
- `%i%` подписана стойност в цяло число
- `%f%` число с плаваща запетая
- `%h%` една или повече цифри HEX
- `%w%` един или повече буквено-цифрови знаци
- `%%` един символ %

Примери:

```php
# Again, hexadecimal number test
Assert::match('%h%', $var);

# Generalized path to file and line number
Assert::match('Error in file %a% on line %i%', $errorMessage);
```


Assert::matchFile(string $file, $actual, ?string $description=null) .[method]
-----------------------------------------------------------------------------
Изпълнението е идентично с това на [Assert::match() |#Assert-match], но шаблонът е зареден от `$file`. Това е полезно за тестване на много дълги низове. Тестовият файл става четим.


Assert::fail(string $message, $actual=null, $expected=null) .[method]
---------------------------------------------------------------------
Това твърдение винаги е неуспешно. Това е просто удобно. Ако е необходимо, можем да предадем очаквани и действителни стойности.


Очаквания .[#toc-expectations]
------------------------------
Ако искаме да сравним по-сложни структури с непостоянни елементи, горните твърдения може да не са достатъчни. Например, тестваме метод, който създава нов потребител и връща атрибутите му като масив. Не знаем хеш стойността на паролата, но знаем, че тя трябва да е шестнадесетичен низ. А всичко, което знаем за следващия елемент, е, че той трябва да бъде обектът `DateTime`.

В тези случаи можем да използваме `Tester\Expect` вътре в параметъра `$expected` методи `Assert::equal()` и `Assert::notEqual()`, с които лесно можем да опишем структурата.

```php
use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # we expect an integer
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # we expect a string matching pattern
	'created_at' => Expect::type(DateTime::class), # we expect an instance of the class
], User::create(123, 'milo', 'RandomPaSsWoRd'));
```

С `Expect` можем да направим почти същите твърдения като с `Assert`. Така че имаме методи като `Expect::same()`, `Expect::match()`, `Expect::count()` и т.н. Освен това можем да ги обединим по следния начин:

```php
Expect::type(MyIterator::class)->andCount(5);  # we expect MyIterator and items count is 5
```

Или можем да напишем свои собствени обработчици на изявления.

```php
Expect::that(function ($value) {
	# return false if expectation fails
});
```


Разследване на неуспешни твърдения .[#toc-failed-assertions-investigation]
--------------------------------------------------------------------------
Tester показва къде се намира грешката, когато дадено твърдение не успее. Когато сравняваме сложни структури, Tester създава дъмпове на сравняваните стойности и ги записва в директорията `output`. Например, когато въображаемият тест `Arrays.recursive.phpt` се провали, изхвърлянията ще бъдат записани по следния начин:

```
app/
└── tests/
	├── output/
	│ ├──── Arrays.recursive.actual   # фактическое значение
	│ └──── Arrays.recursive.expected # ожидаемое значение
	│
	└── Arrays.recursive.phpt         # неудачный тест
```

Можем да променим името на директорията на `Tester\Dumper::$dumpDir`.

Искове

Твърденията се използват за потвърждаване, че действителната стойност съответства на очакваната стойност. Това са методи Tester\Assert.

Изберете най-точните твърдения. По-добре Assert::same($a, $b), отколкото Assert::true($a === $b), защото при неуспех извежда смислено съобщение за грешка. Във втория случай получаваме само false should be true, а той не казва нищо за съдържанието на променливите $a и $b.

Повечето оператори могат да имат и незадължителен $description, който се появява в съобщението за грешка, ако не успеят.

Примерите предполагат, че е дефиниран следният псевдоним на клас:

use Tester\Assert;

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

$expected трябва да бъде същото като $actual. Това е същото като оператора на PHP ===.

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

Противоположен на Assert::same(), така че е същият като оператора на PHP !==.

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

$expected трябва да бъде същото като $actual. За разлика от Assert::same(), идентичността на обектите, редът на двойките ключ ⇒ стойност в масивите и леко различаващите се десетични числа се игнорират, което може да се промени чрез задаване на $matchIdentity и $matchOrder.

Следните случаи са идентични за equal(), но не и за same():

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

Предупреждаваме ви обаче, че масивът [1, 2] и [2, 1] не са еднакви, тъй като се различава само редът на стойностите, а не двойките ключ ⇒ стойност. Масивът [1, 2] може да се запише и като [0 => 1, 1 => 2] и следователно [1 => 2, 0 => 1] ще се считат за равни.

Можете да използвате и т.нар. изчакване в $expected.

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

Контраст Assert::equal().

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

Ако $actual е низ, той трябва да съдържа подниз от $needle. Ако е масив, той трябва да съдържа елемент $needle (сравнява се стриктно).

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

Обратното на Assert::contains().

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

$actual трябва да бъде масив и да съдържа ключа $needle.

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

$actual трябва да бъде масив и да не съдържа ключа $needle.

Assert::true($value, ?string $description=null)

$value трябва да бъде true, така че $value === true.

Assert::truthy($value, ?string $description=null)

$value трябва да е вярно, така че то отговаря на условието if ($value) ....

Assert::false($value, ?string $description=null)

$value трябва да бъде false, следователно $value === false.

Assert::falsey($value, ?string $description=null)

$value трябва да е false, така че то отговаря на условието if (!$value) ....

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

$value трябва да бъде null, така че $value === null.

Assert::notNull($value, ?string $description=null)

$value не следва да бъде null, следователно $value !== null.

Assert::nan($value, ?string $description=null)

$value трябва да бъде Не е число. Използвайте Assert::nan() само за тестване на NAN. Стойността на NAN е много специфична и изявленията Assert::same() или Assert::equal() могат да се държат непредсказуемо.

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

Броят на елементите в $value трябва да е равен на $count. Това е същото като count($value) === $count.

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

$value трябва да е от посочения тип. Като $type можем да използваме символа:

  • array
  • list е масив, индексиран във възходящ цифров ред на ключовете от нула.
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • директно име на клас или обект, тогава трябва да предадете $value instanceof $type

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

При извикване на $callable трябва да се хвърли изключение за $class. Ако подадем $message, съобщението за изключение трябва да съвпадне. И ако подадем $code, кодът на изключението трябва да е същият.

Например този тест не успява, защото съобщението за изключение не съвпада:

Assert::exception(
	fn() => throw new App\InvalidValueException('Нулевое значение'),
	App\InvalidValueException::class,
	'Значение слишком мало',
);

Assert::exception() връща хвърлено изключение, за да можете да проверите вложеното изключение.

$e = Assert::exception(
	fn() => throw new MyException('Что-то не так', 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)

Проверява дали извикването на $callable генерира очакваните грешки (т.е. предупреждения, известия и т.н.). Като $type посочваме една от константите E_..., например E_WARNING. А ако подадем $message, съобщението за грешка също трябва да съответства на шаблона. Например:

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

Ако обратното извикване генерира повече грешки, трябва да ги очакваме в точен ред. В този случай предаваме масива на $type:

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

Ако $type е име на клас, това изявление се държи по същия начин като Assert::exception().

Assert::noError(callable $callable)

Проверява дали $callable не изхвърля никакви предупреждения/бележки/грешки или изключения на PHP. Това е полезно за проверка на части от кода, в които няма други оператори.

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

$actual трябва да съвпада с $pattern. Можем да използваме две опции за шаблони: регулярни изрази или заместващи символи.

Ако подадем регулярен израз като $pattern, трябва да използваме ~ or #, за да го отделим. Други разделители не се поддържат. Например тест, в който $var трябва да съдържа само шестнадесетични цифри:

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

Друга възможност е подобна на сравнението на низове, но можем да използваме някои заместващи символи в $pattern:

  • %a% един или повече символи, с изключение на символите за край на реда
  • %a?% нула или повече символи, с изключение на символите за край на реда
  • %A% един или повече от всички символи, включително символите за край на реда
  • %A?% нула или повече символа, включително символите за край на реда
  • %s% един или повече интервали, с изключение на символите за край на реда
  • %s?% нула или повече интервали, с изключение на символите за край на реда
  • %S% един или повече знаци, с изключение на интервал
  • %S?% нула или повече знаци, с изключение на интервал
  • %c% една или повече цифри от всякакъв вид (с изключение на края на реда)
  • %d% една или повече цифри
  • %d?% нула или повече цифри
  • %i% подписана стойност в цяло число
  • %f% число с плаваща запетая
  • %h% една или повече цифри HEX
  • %w% един или повече буквено-цифрови знаци
  • %% един символ %

Примери:

# Again, hexadecimal number test
Assert::match('%h%', $var);

# Generalized path to file and line number
Assert::match('Error in file %a% on line %i%', $errorMessage);

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

Изпълнението е идентично с това на Assert::match(), но шаблонът е зареден от $file. Това е полезно за тестване на много дълги низове. Тестовият файл става четим.

Assert::fail(string $message, $actual=null, $expected=null)

Това твърдение винаги е неуспешно. Това е просто удобно. Ако е необходимо, можем да предадем очаквани и действителни стойности.

Очаквания

Ако искаме да сравним по-сложни структури с непостоянни елементи, горните твърдения може да не са достатъчни. Например, тестваме метод, който създава нов потребител и връща атрибутите му като масив. Не знаем хеш стойността на паролата, но знаем, че тя трябва да е шестнадесетичен низ. А всичко, което знаем за следващия елемент, е, че той трябва да бъде обектът DateTime.

В тези случаи можем да използваме Tester\Expect вътре в параметъра $expected методи Assert::equal() и Assert::notEqual(), с които лесно можем да опишем структурата.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # we expect an integer
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # we expect a string matching pattern
	'created_at' => Expect::type(DateTime::class), # we expect an instance of the class
], User::create(123, 'milo', 'RandomPaSsWoRd'));

С Expect можем да направим почти същите твърдения като с Assert. Така че имаме методи като Expect::same(), Expect::match(), Expect::count() и т.н. Освен това можем да ги обединим по следния начин:

Expect::type(MyIterator::class)->andCount(5);  # we expect MyIterator and items count is 5

Или можем да напишем свои собствени обработчици на изявления.

Expect::that(function ($value) {
	# return false if expectation fails
});

Разследване на неуспешни твърдения

Tester показва къде се намира грешката, когато дадено твърдение не успее. Когато сравняваме сложни структури, Tester създава дъмпове на сравняваните стойности и ги записва в директорията output. Например, когато въображаемият тест Arrays.recursive.phpt се провали, изхвърлянията ще бъдат записани по следния начин:

app/
└── tests/
	├── output/
	│ ├──── Arrays.recursive.actual   # фактическое значение
	│ └──── Arrays.recursive.expected # ожидаемое значение
	│
	└── Arrays.recursive.phpt         # неудачный тест

Можем да променим името на директорията на Tester\Dumper::$dumpDir.