Afirmații

Afirmațiile sunt utilizate pentru a afirma că o valoare reală corespunde unei valori așteptate. Acestea sunt metode ale aplicației Tester\Assert.

Alegeți cele mai exacte aserțiuni. Este mai bună Assert::same($a, $b) decât Assert::true($a === $b), deoarece afișează un mesaj de eroare semnificativ în caz de eșec. În cel de-al doilea caz, obținem doar false should be true și nu spune nimic despre conținutul variabilelor $a și $b.

Cele mai multe aserțiuni pot avea, de asemenea, un $description opțional care apare în mesajul de eroare în cazul în care așteptarea eșuează.

Exemplele presupun că este definit următorul alias de clasă:

use Tester\Assert;

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

$expected trebuie să fie aceeași cu $actual. Este același lucru cu operatorul PHP ===.

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

Opusul lui Assert::same(), deci este același lucru cu operatorul PHP !==.

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

$expected trebuie să fie același cu $actual. Spre deosebire de Assert::same(), identitatea obiectului, ordinea perechilor cheie ⇒ valoare în array-uri și numerele zecimale marginal diferite sunt ignorate, care pot fi modificate prin setarea $matchIdentity și $matchOrder.

Următoarele cazuri sunt identice din punctul de vedere al equal(), dar nu și pentru same():

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

Cu toate acestea, atenție, matricea [1, 2] și [2, 1] nu sunt egale, deoarece doar ordinea valorilor diferă, nu și perechile cheie ⇒ valoare. Tabloul [1, 2] poate fi scris și sub forma [0 => 1, 1 => 2] și deci [1 => 2, 0 => 1] va fi considerat egal.

Puteți utiliza, de asemenea, așa-numitele așteptări în $expected.

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

Opusul lui Assert::equal().

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

Dacă $actual este un șir de caractere, acesta trebuie să conțină subșirul $needle. Dacă este o matrice, trebuie să conțină elementul $needle (acesta este comparat strict).

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

Opusul lui Assert::contains().

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

$actual trebuie să fie o matrice și să conțină cheia $needle.

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

$actual trebuie să fie o matrice și să nu conțină cheia $needle.

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

$value trebuie să fie true, deci $value === true.

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

$value trebuie să fie adevărat, deci satisface condiția if ($value) ....

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

$value trebuie să fie false, deci $value === false.

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

$value trebuie să fie falsey, deci îndeplinește condiția if (!$value) ....

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

$value trebuie să fie null, deci $value === null.

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

$value nu trebuie să fie null, deci $value !== null.

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

$value trebuie să nu fie un număr. Utilizați numai Assert::nan() pentru testarea NAN. Valoarea NAN este foarte specifică, iar afirmațiile Assert::same() sau Assert::equal() se pot comporta în mod imprevizibil.

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

Numărul de elemente din $value trebuie să fie $count. Deci la fel ca count($value) === $count.

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

$value trebuie să fie de un anumit tip. La fel ca $type putem folosi string:

  • array
  • list – matrice indexată în ordine crescătoare a cheilor numerice de la zero.
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • numele clasei sau obiectul direct, atunci trebuie să treacă $value instanceof $type

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

La invocarea $callable trebuie să se arunce o excepție de instanță $class. Dacă se trece $message, mesajul excepției trebuie să corespundă. Iar dacă se trece $code, codul excepției trebuie să fie același.

De exemplu, acest test eșuează deoarece mesajul excepției nu se potrivește:

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

Site-ul Assert::exception() returnează o excepție lansată, astfel încât puteți testa o excepție imbricata.

$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)

Verifică dacă invocarea $callable generează erorile așteptate (adică avertismente, notificări etc.). Ca $type se specifică una dintre constantele E_..., de exemplu E_WARNING. Iar dacă se trece $message, mesajul de eroare trebuie să corespundă și el modelului. De exemplu:

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

Dacă callback-ul generează mai multe erori, trebuie să le așteptăm pe toate în ordinea exactă. În acest caz, trecem matricea în $type:

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

Dacă $type este numele clasei, această afirmație se comportă la fel ca Assert::exception().

Assert::noError (callable $callable)

Verifică dacă funcția $callable nu aruncă niciun avertisment/notificare/error sau excepție PHP. Este utilă pentru testarea unei bucăți de cod în care nu există nicio altă afirmație.

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

$actual trebuie să se potrivească cu $pattern. Putem utiliza două variante de tipare: expresii regulate sau caractere sălbatice.

Dacă trecem o expresie regulată ca $pattern, trebuie să folosim ~ or # pentru a o delimita. Alte delimitatoare nu sunt acceptate. De exemplu, testul în care $var trebuie să conțină numai cifre hexazecimale:

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

Cealaltă variantă este similară cu compararea șirurilor de caractere, dar putem folosi unele caractere wild în $pattern:

  • %a% unul sau mai multe din orice, cu excepția caracterelor de sfârșit de linie
  • %a?% zero sau mai multe de orice, cu excepția caracterelor de sfârșit de linie
  • %A% unul sau mai multe din orice, inclusiv caracterele de sfârșit de linie
  • %A?% zero sau mai multe de orice, inclusiv caracterele de sfârșit de linie
  • %s% unul sau mai multe caractere de spațiu alb, cu excepția caracterelor de sfârșit de rând
  • %s?% zero sau mai multe caractere de spațiu alb, cu excepția caracterelor de sfârșit de rând
  • %S% unul sau mai multe caractere, cu excepția spațiului alb
  • %S?% zero sau mai multe caractere, cu excepția spațiului alb
  • %c% un singur caracter de orice fel (cu excepția celor de sfârșit de rând)
  • %d% una sau mai multe cifre
  • %d?% zero sau mai multe cifre
  • %i% valoare întreagă cu semn
  • %f% număr în virgulă mobilă
  • %h% una sau mai multe cifre HEX
  • %w% unul sau mai multe caractere alfanumerice
  • %% un caracter %

Exemple:

# 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)

Afirmația este identică cu Assert::match(), dar modelul este încărcat din $file. Este utilă pentru testarea șirurilor foarte lungi. Fișierul de test stă în picioare lizibil.

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

Această afirmație eșuează întotdeauna. Este doar la îndemână. Putem trece opțional valorile așteptate și cele reale.

Așteptări

Dacă dorim să comparăm structuri mai complexe, cu elemente neconstante, afirmațiile de mai sus ar putea să nu fie suficiente. De exemplu, testăm o metodă care creează un nou utilizator și îi returnează atributele sub forma unui tablou. Nu cunoaștem valoarea hash a parolei, dar știm că aceasta trebuie să fie un șir hexazecimal. Și singurul lucru pe care îl știm despre următorul element este că trebuie să fie un obiect DateTime.

În aceste cazuri, putem utiliza Tester\Expect în interiorul parametrului $expected al metodelor Assert::equal() și Assert::notEqual(), care poate fi folosit pentru a descrie cu ușurință structura.

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'));

Cu Expect, putem face aproape aceleași afirmații ca și cu Assert. Astfel, avem metode precum Expect::same(), Expect::match(), Expect::count(), etc. În plus, putem să le înlănțuim astfel:

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

Sau, putem scrie propriile gestionari de afirmații.

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

Investigarea aserțiunilor eșuate

Tester arată unde se află eroarea atunci când o aserțiune eșuează. Atunci când comparăm structuri complexe, Tester creează descărcări ale valorilor comparate și le salvează în directorul output. De exemplu, atunci când testul imaginar Arrays.recursive.phpt eșuează, dumps-urile vor fi salvate după cum urmează:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # actual value
	│   └── Arrays.recursive.expected  # expected value
	│
	└── Arrays.recursive.phpt          # failing test

Putem schimba numele directorului prin Tester\Dumper::$dumpDir.