Затвердження
Твердження використовуються для підтвердження того, що
фактичне значення відповідає очікуваному значенню. Вони є методами
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
має бути хибним, тому він задовольняє умову
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
має бути Not a Number. Використовуйте тільки 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
.