Asercja
Asercje są używane do potwierdzenia, że rzeczywista wartość pasuje do wartości oczekiwanej. Są to metody
klasy Tester\Assert
.
Wybierz najbardziej odpowiednie twierdzenia. Jest to lepsze Assert::same($a, $b)
niż
Assert::true($a === $b)
, ponieważ wyświetli sensowny komunikat o błędzie, gdy się nie powiedzie. W tym drugim
przypadku tylko false should be true
, który nie mówi nam nic o zawartości zmiennych $a
i
$b
.
Również większość asercji może mieć opcjonalną etykietę w parametrze $description
, która zostanie
wyświetlona w komunikacie o błędzie, jeśli oczekiwanie nie powiedzie się.
W przykładach założono, że alias został utworzony:
use Tester\Assert;
Assert::same($expected, $actual, string $description=null)
$expected
musi być identyczny z $actual
. Identyczny jak operator PHP ===
.
Assert::notSame($expected, $actual, string $description=null)
Przeciwieństwo Assert::same()
, czyli to samo co operator PHP !==
.
Assert::equal($expected, $actual, string $description=null, bool $matchOrder=false, bool $matchIdentity=false)
$expected
musi być taka sama jak $actual
. W przeciwieństwie do Assert::same()
,
tożsamość obiektu, kolejność par kluczy ⇒ wartości w polach i marginalnie różne liczby dziesiętne są ignorowane, co
można zmienić poprzez ustawienie $matchIdentity
i $matchOrder
.
Poniższe przypadki są identyczne z perspektywy equal()
, ale nie same()
:
Assert::equal(0.3, 0.1 + 0.2);
Assert::equal($obj, clone $obj);
Assert::equal(
['first' => 11, 'second' => 22],
['second' => 22, 'first' => 11],
);
Jednakże, uważaj, pole [1, 2]
a [2, 1]
nie są takie same, ponieważ tylko kolejność wartości
jest inna, a nie pary klucz ⇒ wartość. Pole [1, 2]
można również zapisać jako
[0 => 1, 1 => 2]
i dlatego będą uważane za takie same [1 => 2, 0 => 1]
.
Ponadto w $expected
można wykorzystać tzw. oczekiwania.
Assert::notEqual($expected, $actual, string $description=null)
Przeciwieństwo Assert::equal()
.
Assert::contains($needle, string|array $actual, string $description=null)
Jeśli $actual
jest ciągiem, musi zawierać podłańcuch $needle
. Jeśli jest to tablica, musi
zawierać element $needle
(dopasowany ściśle).
Assert::notContains($needle, string|array $actual, string $description=null)
Przeciwieństwo Assert::contains()
.
Assert::hasKey(string|int $needle, array $actual, string $description=null)
$actual
musi być tablicą i musi zawierać klucz $needle
.
Assert::notHasKey(string|int $needle, array $actual, string $description=null)
$actual
musi być tablicą i nie może zawierać klucza $needle
.
Assert::true($value, string $description=null)
$value
musi być true
, czyli $value === true
.
Assert::truthy($value, string $description=null)
$value
musi być prawdziwa, czyli spełnia warunek if ($value) ...
.
Assert::false($value, string $description=null)
$value
musi być false
, czyli $value === false
.
Assert::falsey($value, string $description=null)
$value
musi być fałszywy, czyli spełnia warunek if (!$value) ...
.
Assert::null($value, string $description=null)
$value
musi być null
, czyli $value === null
.
Assert::notNull($value, string $description=null)
$value
nie może być null
, czyli $value !== null
.
Assert::nan($value, string $description=null)
$value
musi być „Not a Number“. Do testowania wartości NAN używaj wyłącznie Assert::nan()
Wartość NAN jest bardzo specyficzna i asercje Assert::same()
lub Assert::equal()
mogą działać
nieoczekiwanie.
Assert::count($count, Countable|array $value, string $description=null)
Liczba elementów w $value
musi być $count
. Zatem taka sama jak
count($value) === $count
.
Assert::type(string|object $type, $value, string $description=null)
$value
musi być danego typu. Jako $type
możemy użyć ciągu znaków:
array
list
– tablica indeksowana rosnącym szeregiem kluczy numerycznych od zerabool
callable
float
int
null
object
resource
scalar
string
- nazwa klasy lub samego obiektu, to musi być
$value instanceof $type
Assert::exception(callable $callable, string $class, string $message=null, $code=null)
Po wywołaniu $callable
musi zostać rzucony wyjątek klasowy $class
Jeśli podamy
$message
, komunikat wyjątku musi pasować do wzorca, a jeśli podamy
$code
, kody muszą być ściśle dopasowane.
Poniższy test kończy się niepowodzeniem, ponieważ komunikat wyjątku nie pasuje:
Assert::exception(
fn() => throw new App\InvalidValueException('Zero value'),
App\InvalidValueException::class,
'Value is to low',
);
Assert::exception()
zwraca rzucony wyjątek, więc można przetestować zagnieżdżony wyjątek.
$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)
Sprawdza, czy funkcja $callable
wygenerowała oczekiwane błędy (tj. Ostrzeżenia, powiadomienia itp.). Jako
$type
podajemy jedną ze stałych E_...
, czyli np. E_WARNING
. A jeśli określimy
$message
, to komunikat o błędzie musi pasować do wzorca. Na przykład:
Assert::error(
fn() => $i++,
E_NOTICE,
'Undefined variable: i',
);
Jeśli callback generuje wiele błędów, musimy oczekiwać ich wszystkich w dokładnej kolejności. W tym przypadku
przekazujemy pole $type
:
Assert::error(function () {
$a++;
$b++;
}, [
[E_NOTICE, 'Undefined variable: a'],
[E_NOTICE, 'Undefined variable: b'],
]);
Jeśli określisz nazwę klasy jako $type
, zachowuje się ona tak samo jak
Assert::exception()
.
Assert::noError(callable $callable)
Sprawdza, czy funkcja $callable
nie wygenerowała żadnych ostrzeżeń, błędów lub wyjątków. Jest to
przydatne do testowania fragmentów kodu, w których nie ma innej asercji.
Assert::match(string $pattern, $actual, string $description=null)
$actual
musi pasować do wzorca $pattern
. Możemy użyć dwóch wariantów wzorców: wyrażeń
regularnych lub symboli wieloznacznych.
Jeśli przekażemy wyrażenie regularne jako $pattern
, musimy użyć ~
nebo #
do jego
delimitacji, inne delimitery nie są obsługiwane. Na przykład test, w którym $var
musi zawierać tylko cyfry
szesnastkowe:
Assert::match('#^[0-9a-f]$#i', $var);
Druga opcja jest podobna do dopasowywania ciągów regularnych, ale możemy używać różnych znaków wieloznacznych w
$pattern
:
%a%
jeden lub więcej znaków, z wyjątkiem znaków końca linii%a?%
żaden lub więcej znaków, z wyjątkiem znaków końca linii%A%
jeden lub więcej znaków, w tym znaki końca linii%A?%
żaden lub więcej znaków, w tym znaki końca linii%s%
jeden lub więcej białych znaków, z wyłączeniem znaków końca linii%s?%
żaden lub więcej białych znaków, z wyjątkiem znaków końca linii%S%
jeden lub więcej znaków, z wyłączeniem białych znaków%S?%
żaden lub więcej znaków, z wyjątkiem białych znaków%c%
dowolny znak, z wyjątkiem znaków przerwy w linii%d%
jedna lub więcej cyfr%d?%
brak lub więcej cyfr%i%
podpisana wartość całkowita%f%
liczba z kropką dziesiętną%h%
jedna lub więcej cyfr szesnastkowych%w%
jeden lub więcej znaków alfanumerycznych%%
znak %
Przykłady:
# Opět test na hexadecimální číslo
Assert::match('%h%', $var);
# Zobecnění cesty k souboru a čísla řádky
Assert::match('Error in file %a% on line %i%', $errorMessage);
Assert::matchFile(string $file, $actual, string $description=null)
Asercja jest identyczna jak Assert::match(), ale wzór jest odczytywany z $file
.
Jest to przydatne do testowania bardzo długich łańcuchów. Plik testowy pozostaje przezroczysty.
Assert::fail(string $message, $actual=null, $expected=null)
To twierdzenie zawsze się nie sprawdza. Czasami po prostu się przydaje. Opcjonalnie możemy dołączyć wartość oczekiwaną i rzeczywistą.
Oczekiwania
Gdy chcemy porównywać bardziej złożone struktury z elementami niestałymi, powyższe twierdzenia mogą okazać się
niewystarczające. Na przykład testujemy metodę, która tworzy nowego użytkownika i zwraca jego atrybuty jako tablicę. Nie
znamy wartości hashowej hasła, ale wiemy, że musi to być ciąg szesnastkowy. A o następnym elemencie wiemy tylko tyle, że
musi to być obiekt DateTime
.
W takich sytuacjach możemy użyć Tester\Expect
wewnątrz parametru $expected
metod
Assert::equal()
i Assert::notEqual()
, za pomocą których możemy w prosty sposób opisać
strukturę.
use Tester\Expect;
Assert::equal([
'id' => Expect::type('int'), # očekáváme celé číslo
'username' => 'milo',
'password' => Expect::match('%h%'), # očekáváme řetězec vyhovující vzoru
'created_at' => Expect::type(DateTime::class), # očekáváme instanci třídy
], User::create(123, 'milo', 'RandomPaSsWoRd'));
Za pomocą Expect
możemy wykonać prawie takie same asercje jak za pomocą Assert
. Tym samym
dostępne są dla nas metody Expect::same()
, Expect::match()
, Expect::count()
, itd. Co
więcej, możemy je konkatenować:
Expect::type(MyIterator::class)->andCount(5); # očekáváme MyIterator a počet prvků 5
Możemy też napisać własne assertion handlers.
Expect::that(function ($value) {
# vrátíme false, pokud očekávání selže
});
Badanie błędnych twierdzeń
Kiedy asercja się nie powiedzie, Tester wymienia, co to za błąd. Przy porównywaniu bardziej złożonych struktur Tester
tworzy atrapy porównywanych wartości i przechowuje je w katalogu output
. Na przykład, jeśli test manekinów
Arrays.recursive.phpt
nie powiedzie się, manekiny zostaną zapisane w następujący sposób:
app/
└── tests/
├── output/
│ ├── Arrays.recursive.actual # aktuální hodnota
│ └── Arrays.recursive.expected # očekávaná hodnota
│
└── Arrays.recursive.phpt # selhávající test
Nazwę katalogu możemy zmienić poprzez stronę Tester\Dumper::$dumpDir
.