Assertion'lar

Assertion'lar, gerçek değerin beklenen değerle eşleştiğini onaylamak için kullanılır. Tester\Assert sınıfının metotlarıdır.

En uygun assertion'ı seçin. Assert::same($a, $b), Assert::true($a === $b) ifadesinden daha iyidir; çünkü başarısızlıkta anlamlı bir hata mesajı gösterir. İkinci durumda yalnızca false should be true alırız ve bu, $a ile $b değişkenlerinin içeriği hakkında bize hiçbir şey söylemez.

Assertion'ların çoğu, beklenti sağlanmazsa hata mesajında görüntülenen isteğe bağlı bir $description parametresi de alabilir.

Örnekler, şu takma adın oluşturulduğunu varsayar:

use Tester\Assert;

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

$expected, $actual ile özdeş olmalıdır. PHP'nin === operatörüyle aynıdır.

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

Assert::same() metodunun tersi; yani PHP'nin !== operatörüyle aynıdır.

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

$expected, $actual değerine eşit olmalıdır. Assert::same() metodunun aksine nesne özdeşliği, dizilerdeki anahtar ⇒ değer çiftlerinin sırası ve çok az farklı ondalık sayılar yok sayılır; bu, $matchIdentity ve $matchOrder ayarlanarak değiştirilebilir.

Şu durumlar equal() açısından eşittir, ama same() açısından değildir:

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

Ama dikkat: [1, 2] ve [2, 1] dizileri aynı değildir; çünkü yalnızca değerlerin sırası farklıdır, anahtar ⇒ değer çiftlerinin değil. [1, 2] dizisi [0 => 1, 1 => 2] olarak da yazılabilir, dolayısıyla [1 => 2, 0 => 1] aynı sayılır.

$expected içinde Beklentiler denen yapıları da kullanabilirsiniz.

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

Assert::equal() metodunun tersi.

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

$actual bir dizeyse, $needle alt dizesini içermelidir. Bir diziyse, $needle öğesini içermelidir (katı karşılaştırma yapılır).

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

Assert::contains() metodunun tersi.

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

$actual bir dizi olmalı ve $needle anahtarını içermelidir.

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

$actual bir dizi olmalı ve $needle anahtarını içermemelidir.

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

$value, true olmalıdır; yani $value === true.

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

$value doğruluk değeri taşımalıdır; yani if ($value) ... koşulunu sağlamalıdır.

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

$value, false olmalıdır; yani $value === false.

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

$value yanlışlık değeri taşımalıdır; yani if (!$value) ... koşulunu sağlamalıdır.

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

$value, null olmalıdır; yani $value === null.

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

$value, null olmamalıdır; yani $value !== null.

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

$value, Not a Number olmalıdır. NAN değerlerini test etmek için yalnızca Assert::nan() kullanın. NAN değeri çok özeldir ve Assert::same() ya da Assert::equal() gibi assertion'lar beklenmedik davranabilir.

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

$value içindeki öğe sayısı $count olmalıdır. count($value) === $count ile aynıdır.

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

$value verilen türde olmalıdır. $type olarak bir dize kullanabiliriz:

  • array
  • list – sıfırdan başlayan artan sayısal anahtarlarla indekslenmiş dizi
  • bool
  • callable
  • float
  • int
  • null
  • object
  • resource
  • scalar
  • string
  • sınıf adı ya da doğrudan bir nesne; o zaman $value instanceof $type olmalıdır

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

$callable çağrıldığında $class sınıfından bir istisna fırlatılmalıdır. $message belirtirsek, istisnanın mesajı da desene uymalıdır. $code belirtirsek, kodlar da tam olarak eşleşmelidir.

Örneğin aşağıdaki test, istisna mesajı uymadığı için başarısız olacak:

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

Assert::exception(), fırlatılan istisnayı döndürür; böylece iç içe bir istisnayı da test edebilirsiniz.

$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 fonksiyonunun beklenen hataları (yani uyarıları, notice'ları vb.) ürettiğini denetler. $type olarak E_... sabitlerinden birini, örneğin E_WARNING belirtin. $message belirtirsek, hata mesajı da desene uymalıdır. Örneğin:

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

Callback daha fazla hata üretiyorsa, hepsini tam olarak aynı sırayla beklemeliyiz. Bu durumda $type içinde bir dizi verin:

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

$type olarak sınıf adı belirtirseniz, Assert::exception() ile aynı şekilde davranır.

Assert::noError (callable $callable)

$callable fonksiyonunun hiçbir uyarı, hata ya da istisna üretmediğini denetler. Başka assertion'ın bulunmadığı kod parçalarını test etmek için yararlıdır.

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

$actual, $pattern desenine uymalıdır. Desenlerin iki varyantını kullanabiliriz: düzenli ifadeler ya da joker karakterler.

$pattern olarak bir düzenli ifade verirsek, onu sınırlamak için ~ ya da # kullanmalıyız. Başka sınırlayıcılar desteklenmez. Örneğin $var değişkeninin yalnızca onaltılık rakam içermesi gereken bir test:

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

İkinci varyant sıradan dizeleri karşılaştırmaya benzer, ama $pattern içinde çeşitli joker karakterler kullanabiliriz:

  • %a% satır sonu karakterleri dışında bir ya da daha fazla herhangi bir şey
  • %a?% satır sonu karakterleri dışında sıfır ya da daha fazla herhangi bir şey
  • %A% satır sonu karakterleri dahil bir ya da daha fazla herhangi bir şey
  • %A?% satır sonu karakterleri dahil sıfır ya da daha fazla herhangi bir şey
  • %s% satır sonu karakterleri dışında bir ya da daha fazla boşluk karakteri
  • %s?% satır sonu karakterleri dışında sıfır ya da daha fazla boşluk karakteri
  • %S% boşluk karakterleri dışında bir ya da daha fazla karakter
  • %S?% boşluk karakterleri dışında sıfır ya da daha fazla karakter
  • %c% herhangi bir türden tek bir karakter (satır sonu dışında)
  • %d% bir ya da daha fazla rakam
  • %d?% sıfır ya da daha fazla rakam
  • %i% işaretli tam sayı değeri
  • %f% kayan noktalı sayı
  • %h% bir ya da daha fazla onaltılık rakam
  • %w% bir ya da daha fazla alfanümerik karakter
  • %ds% dizin ayırıcı (/ ya da \)
  • %% bir % karakteri

Örnekler:

# Yine onaltılık sayı testi
Assert::match('%h%', $var);

# Dosya yolunun ve satır numarasının genelleştirilmesi
Assert::match('Error in file %a% on line %i%', $errorMessage);

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

Assert::match() metodunun tersi.

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

Bu assertion Assert::match() ile aynıdır, ama desen $file dosyasından yüklenir. Bu, çok uzun dizeleri test etmek için yararlıdır. Test dosyası derli toplu kalır.

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

Bu assertion her zaman başarısız olur. Bazen tam da işe yarar. İsteğe bağlı olarak beklenen ve gerçek değeri belirtebiliriz.

Beklentiler

Sabit olmayan öğeler içeren daha karmaşık yapıları karşılaştırmak istediğimizde, yukarıda sözü edilen assertion'lar yetmeyebilir. Örneğin yeni bir kullanıcı oluşturup özniteliklerini dizi olarak döndüren bir metodu test ediyoruz. Parola hash'inin değerini bilmiyoruz, ama onun onaltılık bir dize olması gerektiğini biliyoruz. Bir sonraki öğe hakkında ise yalnızca bir DateTime nesnesi olması gerektiğini biliyoruz.

Bu durumlarda, Assert::equal() ve Assert::notEqual() metotlarının $expected parametresinin içinde Tester\Expect kullanabiliriz; onunla yapı kolayca anlatılabilir.

use Tester\Expect;

Assert::equal([
	'id' => Expect::type('int'),                   # bir tam sayı bekliyoruz
	'username' => 'milo',
	'password' => Expect::match('%h%'),            # desene uyan bir dize bekliyoruz
	'created_at' => Expect::type(DateTime::class), # sınıfın bir örneğini bekliyoruz
], User::create(123, 'milo', 'RandomPaSsWoRd'));

Expect ile, Assert ile yaptığımız assertion'ların neredeyse aynısını yapabiliriz. Böylece Expect::same(), Expect::match(), Expect::count() gibi metotlar elimizin altındadır. Ayrıca onları zincirleyebiliriz:

Expect::type(MyIterator::class)->andCount(5);  # MyIterator bekliyoruz ve öğe sayısı 5

Alternatif olarak kendi assertion işleyicilerimizi yazabiliriz.

Expect::that(function ($value) {
	# beklenti sağlanmazsa false döndür
});

Başarısız Assertion'ları İnceleme

Bir assertion başarısız olduğunda Tester, hatanın ne olduğunu yazdırır. Karmaşık yapıları karşılaştırıyorsak, Tester karşılaştırılan değerlerin dökümlerini oluşturur ve output dizinine kaydeder. Örneğin varsayımsal Arrays.recursive.phpt testi başarısız olursa dökümler şöyle saklanır:

app/
└── tests/
	├── output/
	│   ├── Arrays.recursive.actual    # gerçek değer
	│   └── Arrays.recursive.expected  # beklenen değer
	│
	└── Arrays.recursive.phpt          # başarısız test

Dizin adını Tester\Dumper::$dumpDir ile değiştirebiliriz.