Asercije

Asercije se uporabljajo za potrditev, da dejanska vrednost ustreza pričakovani vrednosti. Gre za metode razreda Tester\Assert.

Izbirajte čim bolj primerne asercije. Bolje je Assert::same($a, $b) kot Assert::true($a === $b), ker ob neuspehu prikaže smiselno sporočilo o napaki. V drugem primeru samo false should be true, kar nam o vsebini spremenljivk $a in $b nič ne pove.

Večina asercij lahko ima tudi neobvezen opis v parametru $description, ki se prikaže v sporočilu o napaki, če pričakovanje ne uspe.

Primeri predpostavljajo ustvarjen alias:

use Tester\Assert;

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

$expected mora biti identičen z $actual. Enako kot PHP operator ===.

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

Nasprotje Assert::same(), torej enako kot PHP operator !==.

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

$expected mora biti enak z $actual. Za razliko od Assert::same() se ignorira identiteta objektov, vrstni red parov ključ ⇒ vrednost v poljih in marginalno različna decimalna števila, kar je mogoče spremeniti z nastavitvijo $matchIdentity in $matchOrder.

Naslednji primeri so enaki z vidika equal(), vendar ne same():

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

Vendar pozor, polji [1, 2] in [2, 1] nista enaki, ker se razlikujeta samo v vrstnem redu vrednosti, ne pa parov ključ ⇒ vrednost. Polje [1, 2] lahko zapišemo tudi kot [0 => 1, 1 => 2] in za enako se zato bo štelo [1 => 2, 0 => 1].

Nadalje je mogoče v $expected uporabiti t.i. pričakovanja.

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

Nasprotje Assert::equal().

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

Če je $actual niz, mora vsebovati podniz $needle. Če je polje, mora vsebovati element $needle (primerja se strogo).

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

Nasprotje Assert::contains().

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

$actual mora biti polje in mora vsebovati ključ $needle.

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

$actual mora biti polje in ne sme vsebovati ključa $needle.

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

$value mora biti true, torej $value === true.

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

$value mora biti resničen, torej izpolni pogoj if ($value) ....

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

$value mora biti false, torej $value === false.

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

$value mora biti neresničen, torej izpolni pogoj if (!$value) ....

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

$value mora biti null, torej $value === null.

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

$value ne sme biti null, torej $value !== null.

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

$value mora biti Not a Number. Za testiranje NAN vrednosti uporabljajte izključno Assert::nan(). Vrednost NAN je zelo specifična in aserciji Assert::same() ali Assert::equal() lahko delujeta nepričakovano.

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

Število elementov v $value mora biti $count. Torej enako kot count($value) === $count.

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

$value mora biti danega tipa. Kot $type lahko uporabimo niz:

  • array
  • list – polje, indeksirano po naraščajočem zaporedju numeričnih ključev od nič
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • ime razreda ali neposredno objekt, potem mora biti $value instanceof $type

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

Pri klicu $callable mora biti sprožena izjema razreda $class. Če navedemo $message, mora ustrezati vzorcu tudi sporočilo izjeme in če navedemo $code, se morata strogo ujemati tudi kodi.

Naslednji test ne uspe, ker ne ustreza sporočilo izjeme:

Assert::exception(
	fn() => throw new App\InvalidValueException('Vrednost je prenizka'),
	App\InvalidValueException::class,
	'Vrednost je prenizka',
);

Assert::exception() vrača sproženo izjemo, lahko tako testiramo tudi ugnezdeno izjemo.

$e = Assert::exception(
	fn() => throw new MyException('Nekaj je narobe', 0, new RuntimeException),
	MyException::class,
	'Nekaj je narobe',
);

Assert::type(RuntimeException::class, $e->getPrevious());

Assert::error (string $callable, int|string|array $type, ?string $message=null)

Preverja, da funkcija $callable generira pričakovane napake (tj. opozorila, obvestila itd). Kot $type navedemo eno od konstant E_..., torej na primer E_WARNING. In če navedemo $message, mora ustrezati vzorcu tudi sporočilo o napaki. Na primer:

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

Če callback generira več napak, jih moramo vse pričakovati v točnem vrstnem redu. V takem primeru predamo v $type polje:

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

Če kot $type navedete ime razreda, se obnaša enako kot Assert::exception().

Assert::noError (callable $callable)

Preverja, da funkcija $callable ni generirala nobenega opozorila, napake ali izjeme. Uporabno za testiranje koščkov kode, kjer ni nobene druge asercije.

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

$actual mora ustrezati vzorcu $pattern. Uporabimo lahko dve varianti vzorcev: regularne izraze ali nadomestne znake.

Če kot $pattern predamo regularni izraz, moramo za njegovo omejitev uporabiti ~ ali #, drugi ločilniki niso podprti. Na primer test, kjer $var mora vsebovati samo šestnajstiške števke:

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

Druga varianta je podobna običajnemu primerjanju nizov, vendar lahko v $pattern uporabimo različne nadomestne znake:

  • %a% en ali več znakov, razen znakov konca vrstice
  • %a?% noben ali več znakov, razen znakov konca vrstice
  • %A% en ali več znakov, vključno z znaki konca vrstice
  • %A?% noben ali več znakov, vključno z znaki konca vrstice
  • %s% en ali več belih znakov, razen znakov konca vrstice
  • %s?% noben ali več belih znakov, razen znakov konca vrstice
  • %S% en ali več znakov, razen belih znakov
  • %S?% noben ali več znakov, razen belih znakov
  • %c% katerikoli en znak, razen znaka konca vrstice
  • %d% ena ali več števk
  • %d?% nobena ali več števk
  • %i% predznačena celoštevilska vrednost
  • %f% število z decimalno vejico
  • %h% ena ali več šestnajstiških števk
  • %w% en ali več alfanumeričnih znakov
  • %% znak %

Primeri:

# Ponovno test za šestnajstiško število
Assert::match('%h%', $var);

# Posplošitev poti do datoteke in številke vrstice
Assert::match('Napaka v datoteki %a% v vrstici %i%', $errorMessage);

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

Asercija je identična z Assert::match(), vendar se vzorec nalaga iz datoteke $file. To je uporabno za testiranje zelo dolgih nizov. Datoteka s testom ostane pregledna.

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

Ta asercija vedno ne uspe. Včasih je to preprosto uporabno. Neobvezno lahko navedemo tudi pričakovano in dejansko vrednost.

Pričakovanja

Ko želimo primerjati bolj zapletene strukture z nekonstantnimi elementi, morda zgornje asercije ne bodo zadostovale. Na primer testiramo metodo, ki ustvari novega uporabnika in vrne njegove atribute kot polje. Vrednosti hasha gesla ne poznamo, vendar vemo, da mora biti šestnajstiški niz. In o drugem elementu vemo samo, da mora biti objekt DateTime.

V teh situacijah lahko uporabimo Tester\Expect znotraj $expected parametra metod Assert::equal() in Assert::notEqual(), s pomočjo katerih lahko strukturo enostavno opišemo.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # pričakujemo celo število
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # pričakujemo niz, ki ustreza vzorcu
	'created_at' => Expect::type(DateTime::class), # pričakujemo instanco razreda
], User::create(123, 'milo', 'RandomPaSsWoRd'));

Z Expect lahko izvajamo skoraj enake asercije kot z Assert. Torej so nam na voljo metode Expect::same(), Expect::match(), Expect::count() itd. Poleg tega jih lahko verižimo:

Expect::type(MyIterator::class)->andCount(5);  # pričakujemo MyIterator in število elementov 5

Ali pa lahko pišemo lastne obdelovalce asercij.

Expect::that(function ($value) {
	# vrnemo false, če pričakovanje ne uspe
});

Raziskovanje napačnih asercij

Ko asercija ne uspe, Tester izpiše, v čem je napaka. Če primerjamo bolj zapletene strukture, Tester ustvari izpise primerjanih vrednosti in jih shrani v imenik output. Na primer pri neuspehu izmišljenega testa Arrays.recursive.phpt bodo izpisi shranjeni na naslednji način:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # dejanska vrednost
	│   └── Arrays.recursive.expected  # pričakovana vrednost
	│
	└── Arrays.recursive.phpt          # neuspešen test

Ime imenika lahko spremenimo preko Tester\Dumper::$dumpDir.