-
-
Notifications
You must be signed in to change notification settings - Fork 282
/
assertions.texy
286 lines (202 loc) · 16.4 KB
/
assertions.texy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
Искове
******
.[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`.