Aserțiuni
Aserțiunile sunt folosite pentru a confirma că valoarea reală corespunde valorii așteptate. Acestea sunt
metode ale clasei Tester\Assert
.
Alegeți aserțiunile cele mai potrivite. Este mai bine Assert::same($a, $b)
decât
Assert::true($a === $b)
, deoarece în caz de eșec afișează un mesaj de eroare semnificativ. În al doilea caz,
doar false should be true
, ceea ce nu ne spune nimic despre conținutul variabilelor $a
și
$b
.
Majoritatea aserțiunilor pot avea și o descriere opțională în parametrul $description
, care se va afișa în
mesajul de eroare dacă așteptarea eșuează.
Exemplele presupun crearea unui alias:
use Tester\Assert;
Assert::same ($expected, $actual, ?string $description=null)
$expected
trebuie să fie identic cu $actual
. Același lucru ca operatorul PHP ===
.
Assert::notSame ($expected, $actual, ?string $description=null)
Opusul Assert::same()
, adică același lucru ca operatorul PHP !==
.
Assert::equal ($expected, $actual, ?string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected
trebuie să fie egal cu $actual
. Spre deosebire de Assert::same()
, se ignoră
identitatea obiectelor, ordinea perechilor cheie ⇒ valoare în array-uri și numerele zecimale marginal diferite, ceea ce poate
fi schimbat prin setarea $matchIdentity
și $matchOrder
.
Următoarele cazuri sunt egale din punctul de vedere al equal()
, dar nu și same()
:
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['first' => 11, 'second' => 22],
['second' => 22, 'first' => 11],
);
Atenție însă, array-urile [1, 2]
și [2, 1]
nu sunt egale, deoarece diferă doar ordinea
valorilor, nu și a perechilor cheie ⇒ valoare. Array-ul [1, 2]
poate fi scris și ca
[0 => 1, 1 => 2]
și, prin urmare, [1 => 2, 0 => 1]
va fi considerat egal.
Mai departe, în $expected
se pot folosi așa-numitele așteptări.
Assert::notEqual ($expected, $actual, ?string $description=null)
Opusul Assert::equal()
.
Assert::contains ($needle, string|array $actual, ?string $description=null)
Dacă $actual
este un șir, trebuie să conțină subșirul $needle
. Dacă este un array, trebuie să
conțină elementul $needle
(se compară strict).
Assert::notContains ($needle, string|array $actual, ?string $description=null)
Opusul Assert::contains()
.
Assert::hasKey (string|int $needle, array $actual, ?string $description=null)
$actual
trebuie să fie un array și trebuie să conțină cheia $needle
.
Assert::notHasKey (string|int $needle, array $actual, ?string $description=null)
$actual
trebuie să fie un array și nu trebuie să conțină cheia $needle
.
Assert::true ($value, ?string $description=null)
$value
trebuie să fie true
, adică $value === true
.
Assert::truthy ($value, ?string $description=null)
$value
trebuie să fie adevărat (truthy), adică îndeplinește condiția if ($value) ...
.
Assert::false ($value, ?string $description=null)
$value
trebuie să fie false
, adică $value === false
.
Assert::falsey ($value, ?string $description=null)
$value
trebuie să fie fals (falsey), adică îndeplinește condiția if (!$value) ...
.
Assert::null ($value, ?string $description=null)
$value
trebuie să fie null
, adică $value === null
.
Assert::notNull ($value, ?string $description=null)
$value
nu trebuie să fie null
, adică $value !== null
.
Assert::nan ($value, ?string $description=null)
$value
trebuie să fie Not a Number. Pentru testarea valorii NAN folosiți exclusiv Assert::nan()
.
Valoarea NAN este foarte specifică și aserțiunile Assert::same()
sau Assert::equal()
pot funcționa
neașteptat.
Assert::count ($count, Countable|array $value, ?string $description=null)
Numărul de elemente din $value
trebuie să fie $count
. Adică același lucru ca
count($value) === $count
.
Assert::type (string|object $type, $value, ?string $description=null)
$value
trebuie să fie de tipul dat. Ca $type
putem folosi un șir:
array
list
– array indexat după o serie ascendentă de chei numerice începând de la zerobool
callable
float
int
null
object
resource
scalar
string
- numele clasei sau direct obiectul, atunci
$value
trebuie să fieinstanceof $type
Assert::exception (callable $callable, string $class, ?string $message=null, $code=null)
La apelarea $callable
trebuie să fie aruncată o excepție de clasa $class
. Dacă specificăm
$message
, trebuie să corespundă modelului și mesajul excepției, iar dacă
specificăm $code
, trebuie să se potrivească strict și codurile.
Următorul test va eșua, deoarece mesajul excepției nu corespunde:
Assert::exception(
fn() => throw new App\InvalidValueException('Valoare zero'),
App\InvalidValueException::class,
'Valoarea este prea mică',
);
Assert::exception()
returnează excepția aruncată, astfel se poate testa și o excepție imbricată.
$e = Assert::exception(
fn() => throw new MyException('Ceva este în neregulă', 0, new RuntimeException),
MyException::class,
'Ceva este în neregulă',
);
Assert::type(RuntimeException::class, $e->getPrevious());
Assert::error (string $callable, int|string|array $type, ?string $message=null)
Verifică dacă funcția $callable
a generat erorile așteptate (adică avertismente, notificări etc.). Ca
$type
specificăm una dintre constantele E_...
, de exemplu E_WARNING
. Și dacă specificăm
$message
, trebuie să corespundă modelului și mesajul de eroare. De exemplu:
Assert::error(
fn() => $i++,
E_NOTICE,
'Variabilă nedefinită: i',
);
Dacă callback-ul generează mai multe erori, trebuie să le așteptăm pe toate în ordinea exactă. În acest caz, transmitem
în $type
un array:
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Variabilă nedefinită: a'],
[E_NOTICE, 'Variabilă nedefinită: b'],
]);
Dacă specificați ca $type
numele unei clase, se comportă la fel ca
Assert::exception()
.
Assert::noError (callable $callable)
Verifică dacă funcția $callable
nu a generat niciun avertisment, eroare sau excepție. Este util pentru
testarea bucăților de cod unde nu există nicio altă aserțiune.
Assert::match (string $pattern, $actual, ?string $description=null)
$actual
trebuie să corespundă modelului $pattern
. Putem folosi două variante de modele: expresii
regulate sau substituenți.
Dacă transmitem ca $pattern
o expresie regulată, pentru delimitarea sa trebuie să folosim ~
sau
#
, alți delimitatori nu sunt suportați. De exemplu, testul în care $var
trebuie să conțină doar
cifre hexazecimale:
Assert::match('#^[0-9a-f]$#i', $var);
A doua variantă este similară cu compararea obișnuită a șirurilor, dar în $pattern
putem folosi diferiți
substituenți:
%a%
unul sau mai multe caractere, cu excepția caracterelor de sfârșit de linie%a?%
zero sau mai multe caractere, cu excepția caracterelor de sfârșit de linie%A%
unul sau mai multe caractere, inclusiv caracterele de sfârșit de linie%A?%
zero sau mai multe caractere, inclusiv caracterele de sfârșit de linie%s%
unul sau mai multe caractere spațiu alb, cu excepția caracterelor de sfârșit de linie%s?%
zero sau mai multe caractere spațiu alb, cu excepția caracterelor de sfârșit de linie%S%
unul sau mai multe caractere, cu excepția caracterelor spațiu alb%S?%
zero sau mai multe caractere, cu excepția caracterelor spațiu alb%c%
orice caracter unic, cu excepția caracterului de sfârșit de linie%d%
una sau mai multe cifre%d?%
zero sau mai multe cifre%i%
valoare întreagă cu semn%f%
număr zecimal%h%
una sau mai multe cifre hexazecimale%w%
unul sau mai multe caractere alfanumerice%%
caracterul %
Exemple:
# Din nou test pentru număr hexazecimal
Assert::match('%h%', $var);
# Generalizarea căii către fișier și a numărului liniei
Assert::match('Eroare în fișierul %a% la linia %i%', $errorMessage);
Assert::matchFile (string $file, $actual, ?string $description=null)
Aserțiunea este identică cu Assert::match(), dar modelul se încarcă din fișierul
$file
. Acest lucru este util pentru testarea șirurilor foarte lungi. Fișierul cu testul rămâne lizibil.
Assert::fail (string $message, $actual=null, $expected=null)
Această aserțiune eșuează întotdeauna. Uneori este pur și simplu util. Opțional, putem specifica și valoarea așteptată și cea reală.
Așteptări
Când dorim să comparăm structuri mai complexe cu elemente neconstante, aserțiunile de mai sus s-ar putea să nu fie
suficiente. De exemplu, testăm o metodă care creează un nou utilizator și returnează atributele sale ca array. Nu cunoaștem
valoarea hash-ului parolei, dar știm că trebuie să fie un șir hexazecimal. Și despre un alt element știm doar că trebuie
să fie un obiect DateTime
.
În aceste situații putem folosi Tester\Expect
în interiorul parametrului $expected
al metodelor
Assert::equal()
și Assert::notEqual()
, cu ajutorul cărora structura poate fi descrisă ușor.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # așteptăm un număr întreg
'username' => 'milo',
'password' => Expect::match('%h%'), # așteptăm un șir care corespunde modelului
'created_at' => Expect::type(DateTime::class), # așteptăm o instanță a clasei
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Cu Expect
putem efectua aproape aceleași aserțiuni ca și cu Assert
. Adică avem la dispoziție
metodele Expect::same()
, Expect::match()
, Expect::count()
etc. În plus, le putem
înlănțui:
Expect::type(MyIterator::class)->andCount(5); # așteptăm MyIterator și numărul de elemente 5
Sau putem scrie proprii handleri de aserțiuni.
Expect::that(function ($value) {
# returnăm false dacă așteptarea eșuează
});
Examinarea aserțiunilor eșuate
Când o aserțiune eșuează, Tester afișează unde este greșeala. Dacă comparăm structuri mai complexe, Tester creează
dump-uri ale valorilor comparate și le salvează în directorul output
. De exemplu, la eșecul testului fictiv
Arrays.recursive.phpt
, dump-urile vor fi salvate astfel:
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # valoarea actuală
│ └── Arrays.recursive.expected # valoarea așteptată
│
└── Arrays.recursive.phpt # testul eșuat
Numele directorului poate fi schimbat prin Tester\Dumper::$dumpDir
.