Ισχυρισμοί

Οι ισχυρισμοί χρησιμοποιούνται για να βεβαιώνουν ότι μια πραγματική τιμή ταιριάζει με μια αναμενόμενη τιμή. Είναι μέθοδοι της Tester\Assert.

Επιλέξτε τους πιο ακριβείς ισχυρισμούς. Είναι καλύτερο Assert::same($a, $b) από το Assert::true($a === $b) επειδή εμφανίζει σημαντικό μήνυμα σφάλματος σε περίπτωση αποτυχίας. Στη δεύτερη περίπτωση παίρνουμε μόνο το false should be true και δεν λέει τίποτα για τα περιεχόμενα των μεταβλητών $a και $b.

Οι περισσότεροι ισχυρισμοί μπορούν επίσης να έχουν ένα προαιρετικό $description που εμφανίζεται στο μήνυμα σφάλματος αν η προσδοκία αποτύχει.

Τα παραδείγματα υποθέτουν ότι έχει οριστεί το ακόλουθο ψευδώνυμο κλάσης:

use Tester\Assert;

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

$expected πρέπει να είναι το ίδιο με το $actual. Είναι το ίδιο με τον τελεστή PHP ===.

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

Αντίθετος του Assert::same(), άρα είναι ίδιος με τον τελεστή PHP !==.

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

$expected πρέπει να είναι το ίδιο με το $actual. Σε αντίθεση με το Assert::same(), η ταυτότητα αντικειμένων, η σειρά των ζευγών κλειδιών ⇒ τιμή σε πίνακες και οι οριακά διαφορετικοί δεκαδικοί αριθμοί αγνοούνται, οι οποίοι μπορούν να αλλάξουν με τη ρύθμιση των $matchIdentity και $matchOrder.

Οι ακόλουθες περιπτώσεις είναι ταυτόσημες από την άποψη του equal(), αλλά όχι για το same():

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] θα θεωρηθεί ίσος.

Μπορείτε επίσης να χρησιμοποιήσετε τις λεγόμενες προσδοκίες στο $expected.

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

Σε αντίθεση με το Assert::equal().

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

Εάν το $actual είναι μια συμβολοσειρά, πρέπει να περιέχει την υποσυμβολοσειρά $needle. Εάν είναι πίνακας, πρέπει να περιέχει το στοιχείο $needle (συγκρίνεται αυστηρά).

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

Αντίθετο από το Assert::contains().

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

$actual πρέπει να είναι ένας πίνακας και να περιέχει το κλειδί $needle.

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

$actual πρέπει να είναι πίνακας και να μην περιέχει το κλειδί $needle.

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

$value πρέπει να είναι true, οπότε $value === true.

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

$value πρέπει να είναι αληθές, άρα ικανοποιεί τη συνθήκη if ($value) ....

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

$value πρέπει να είναι false, άρα $value === false.

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

$value πρέπει να είναι ψευδής, άρα ικανοποιεί τη συνθήκη if (!$value) ....

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

$value πρέπει να είναι null, άρα $value === null.

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

$value δεν πρέπει να είναι null, οπότε $value !== null.

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

$value πρέπει να είναι Not a Number. Χρησιμοποιήστε μόνο το Assert::nan() για δοκιμές NAN. Η τιμή NAN είναι πολύ συγκεκριμένη και οι ισχυρισμοί Assert::same() ή Assert::equal() μπορεί να συμπεριφέρονται απρόβλεπτα.

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

Ο αριθμός των στοιχείων στο $value πρέπει να είναι $count. Άρα το ίδιο με το count($value) === $count.

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

$value πρέπει να είναι συγκεκριμένου τύπου. Ως $type μπορούμε να χρησιμοποιήσουμε το string:

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

Κατά την κλήση του $callable πρέπει να εκπέμπεται μια εξαίρεση της περίπτωσης $class. Αν περάσουμε το $message, το μήνυμα της εξαίρεσης πρέπει να ταιριάζει. Και αν περάσουμε το $code, ο κωδικός της εξαίρεσης πρέπει να είναι ο ίδιος.

Για παράδειγμα, αυτή η δοκιμή αποτυγχάνει επειδή το μήνυμα της εξαίρεσης δεν ταιριάζει:

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

Το Assert::exception() επιστρέφει μια πεταμένη εξαίρεση, οπότε μπορείτε να ελέγξετε μια εμφωλευμένη εξαίρεση.

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

Ελέγχει ότι η κλήση του $callable παράγει τα αναμενόμενα σφάλματα (δηλαδή προειδοποιήσεις, ειδοποιήσεις κ.λπ.). Ως $type καθορίζουμε μία από τις σταθερές E_..., για παράδειγμα E_WARNING. Και αν περάσει το $message, το μήνυμα σφάλματος πρέπει επίσης να ταιριάζει με το πρότυπο. Για παράδειγμα:

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

Εάν η επανάκληση παράγει περισσότερα σφάλματα, πρέπει να τα περιμένουμε όλα με την ακριβή σειρά. Σε αυτή την περίπτωση περνάμε τον πίνακα στο $type:

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

Εάν το $type είναι όνομα κλάσης, αυτή η δήλωση συμπεριφέρεται όπως το Assert::exception().

Assert::noError(callable $callable)

Ελέγχει ότι η συνάρτηση $callable δεν πετάει καμία προειδοποίηση/ειδοποίηση/σφάλμα ή εξαίρεση της PHP. Είναι χρήσιμη για τον έλεγχο ενός κομματιού κώδικα όπου δεν υπάρχει άλλος ισχυρισμός.

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

$actual πρέπει να ταιριάζει με το $pattern. Μπορούμε να χρησιμοποιήσουμε δύο παραλλαγές προτύπων: κανονικές εκφράσεις ή μπαλαντέρ.

Εάν περάσουμε μια κανονική έκφραση ως $pattern, πρέπει να χρησιμοποιήσουμε το ~ or # για να την οριοθετήσουμε. Άλλα διαχωριστικά δεν υποστηρίζονται. Για παράδειγμα, το test όπου το $var πρέπει να περιέχει μόνο δεκαεξαδικά ψηφία:

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

Η άλλη παραλλαγή είναι παρόμοια με τη σύγκριση συμβολοσειρών, αλλά μπορούμε να χρησιμοποιήσουμε κάποια άγρια σύμβολα στο $pattern:

  • %a% ένα ή περισσότερα από οτιδήποτε εκτός από τους χαρακτήρες τέλους γραμμής
  • %a?% μηδέν ή περισσότερα από οτιδήποτε εκτός από τους χαρακτήρες τέλους γραμμής
  • %A% ένα ή περισσότερα από οτιδήποτε, συμπεριλαμβανομένων των χαρακτήρων τέλους γραμμής
  • %A?% μηδέν ή περισσότερα από οτιδήποτε, συμπεριλαμβανομένων των χαρακτήρων τέλους γραμμής
  • %s% ένας ή περισσότεροι χαρακτήρες λευκού διαστήματος εκτός από τους χαρακτήρες τέλους γραμμής
  • %s?% μηδέν ή περισσότεροι λευκοί χαρακτήρες εκτός από τους χαρακτήρες τέλους γραμμής
  • %S% ένας ή περισσότεροι χαρακτήρες εκτός από το λευκό διάστημα
  • %S?% μηδέν ή περισσότεροι χαρακτήρες εκτός από το λευκό διάστημα
  • %c% ένας μόνο χαρακτήρας οποιουδήποτε είδους (εκτός από το τέλος της γραμμής)
  • %d% ένα ή περισσότερα ψηφία
  • %d?% μηδέν ή περισσότερα ψηφία
  • %i% προσημασμένη ακέραια τιμή
  • %f% αριθμός κινητής υποδιαστολής
  • %h% ένα ή περισσότερα ψηφία HEX
  • %w% ένας ή περισσότεροι αλφαριθμητικοί χαρακτήρες
  • %% ένας χαρακτήρας %

Παραδείγματα:

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

Ο ισχυρισμός είναι πανομοιότυπος με την Assert::match() αλλά το μοτίβο φορτώνεται από το $file. Είναι χρήσιμος για τον έλεγχο πολύ μεγάλων συμβολοσειρών. Το αρχείο δοκιμής είναι αναγνώσιμο.

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

Αυτός ο ισχυρισμός πάντα αποτυγχάνει. Είναι απλά πρακτικό. Μπορούμε να περάσουμε προαιρετικά αναμενόμενες και πραγματικές τιμές.

Προσδοκίες

Αν θέλουμε να συγκρίνουμε πιο σύνθετες δομές με μη σταθερά στοιχεία, οι παραπάνω ισχυρισμοί μπορεί να μην είναι επαρκείς. Για παράδειγμα, δοκιμάζουμε μια μέθοδο που δημιουργεί έναν νέο χρήστη και επιστρέφει τα χαρακτηριστικά του ως πίνακα. Δεν γνωρίζουμε την τιμή κατακερματισμού του κωδικού πρόσβασης, αλλά γνωρίζουμε ότι πρέπει να είναι ένα δεκαεξαδικό αλφαριθμητικό. Και το μόνο πράγμα που γνωρίζουμε για το επόμενο στοιχείο είναι ότι πρέπει να είναι ένα αντικείμενο DateTime.

Σε αυτές τις περιπτώσεις, μπορούμε να χρησιμοποιήσουμε το Tester\Expect μέσα στην παράμετρο $expected των μεθόδων Assert::equal() και Assert::notEqual(), το οποίο μπορεί να χρησιμοποιηθεί για την εύκολη περιγραφή της δομής.

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(), κ.λπ. Επιπλέον, μπορούμε να τις αλυσοδέσουμε όπως:

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

Ή, μπορούμε να γράψουμε δικούς μας χειριστές ισχυρισμών.

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

Διερεύνηση αποτυχημένων ισχυρισμών

Ο ελεγκτής δείχνει πού βρίσκεται το σφάλμα όταν ένας ισχυρισμός αποτυγχάνει. Όταν συγκρίνουμε σύνθετες δομές, ο Tester δημιουργεί dumps των συγκρινόμενων τιμών και τις αποθηκεύει στον κατάλογο output. Για παράδειγμα, όταν η φανταστική δοκιμή Arrays.recursive.phpt αποτύχει, τα dumps θα αποθηκευτούν ως εξής:

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

Μπορούμε να αλλάξουμε το όνομα του καταλόγου με Tester\Dumper::$dumpDir.