Yardımcı Sınıflar

HttpAssert

Tester\HttpAssert sınıfı, HTTP sunucularını test etmek için araçlar sunar. Akıcı bir arayüzle HTTP istekleri yapmanızı ve durum kodlarını, header'ları ve yanıt gövdesinin içeriğini kolayca doğrulamanızı sağlar.

# Temel HTTP isteği ve yanıt doğrulaması
$response = Tester\HttpAssert::fetch('https://example.com/api/users');
$response
	->expectCode(200)
	->expectHeader('Content-Type', contains: 'json')
	->expectBody(contains: 'users');

fetch() metodu varsayılan olarak bir GET isteği oluşturur, ama tüm parametreler özelleştirilebilir:

HttpAssert::fetch(
	'https://api.example.com/users',
	method: 'POST',
	headers: [
		'Authorization' => 'Bearer token123',  # ilişkisel dizi
		'Accept: application/json',            # ya da dize biçimi
	],
	cookies: ['session' => 'abc123'],
	follow: false,                             # yönlendirmeleri izleme
	body: '{"name": "John"}'
)
	->expectCode(201);

Durum kodları expectCode() ve denyCode() metotlarıyla doğrulanabilir. Belirli bir sayı ya da bir doğrulama fonksiyonu verebilirsiniz:

$response
	->expectCode(200)                           # tam kod
	->expectCode(fn($code) => $code < 400)      # özel doğrulama
	->denyCode(404)                             # 404 olmamalı
	->denyCode(fn($code) => $code >= 500);      # sunucu hatası olmamalı

Header doğrulaması için expectHeader() ve denyHeader() metotlarını kullanın. Bir header'ın var olup olmadığını denetleyebilir, tam değerini doğrulayabilir ya da içeriğinin bir parçasını eşleştirebilirsiniz:

$response
	->expectHeader('Content-Type')                     # header var olmalı
	->expectHeader('Content-Type', 'application/json') # tam değer
	->expectHeader('Content-Type', contains: 'json')   # metin içerir
	->expectHeader('Server', matches: 'nginx %a%')     # desene uyar
	->denyHeader('X-Powered-By')                       # header olmamalı
	->denyHeader('X-Debug', contains: 'sensitive')     # metin içermemeli
	->denyHeader('X-Debug', matches: '~debug~i');      # desene uymamalı

Yanıt gövdesi doğrulaması expectBody() ve denyBody() metotlarıyla benzer şekilde çalışır:

$response
	->expectBody('OK')                              # tam değer
	->expectBody(contains: '"status": "success"')   # JSON parçası içerir
	->expectBody(matches: '%A% hello %A%')          # desene uyar
	->expectBody(fn($body) => json_decode($body) !== null) # özel doğrulama
	->denyBody('Error occurred')                    # tam değere sahip olmamalı
	->denyBody(contains: 'error')                   # metin içermemeli
	->denyBody(matches: '~exception|fatal~i');      # desene uymamalı

follow parametresi, HttpAssert'in HTTP yönlendirmelerini nasıl ele alacağını denetler:

# Yönlendirmeyi izlemeden test etme (varsayılan)
HttpAssert::fetch('https://example.com/redirect', follow: false)
	->expectCode(301)
	->expectHeader('Location', 'https://example.com/new-url');

# Tüm yönlendirmeleri son yanıta dek izleme
HttpAssert::fetch('https://example.com/redirect', follow: true)
	->expectCode(200)
	->expectBody(contains: 'final content');

DomQuery

Tester\DomQuery, SimpleXMLElement sınıfını genişleten ve HTML ya da XML içinde CSS seçicileriyle kolay sorgulama sunan bir sınıftır.

# HTML dizesinden DomQuery oluştur
$dom = Tester\DomQuery::fromHtml('
	<article class="post">
		<h1>Title</h1>
		<div class="content">Text</div>
	</article>
');

# CSS seçicileriyle eleman varlığını test et
Assert::true($dom->has('article.post'));
Assert::true($dom->has('h1'));

# elemanları DomQuery nesnelerinden oluşan bir dizi olarak bul
$headings = $dom->find('h1');
Assert::same('Title', (string) $headings[0]);

# elemanın seçiciye uyup uymadığını test et (2.5.3 sürümünden beri)
$content = $dom->find('.content')[0];
Assert::true($content->matches('div'));
Assert::false($content->matches('p'));

# seçiciye uyan en yakın atayı bul (2.5.5 sürümünden beri)
$article = $content->closest('.post');
Assert::true($article->matches('article'));

XML belgeleri için fromXml() metodunu kullanın:

$dom = Tester\DomQuery::fromXml('<catalog><item>First</item></catalog>');
Assert::true($dom->has('item'));

FileMock

Tester\FileMock, dosyaları bellekte taklit eder ve fopen(), file_get_contents(), parse_ini_file() gibi fonksiyonları kullanan kodu test etmeyi kolaylaştırır. Örnek kullanım:

# Test edilen sınıf
class Logger
{
	public function __construct(
		private string $logFile,
	) {
	}

	public function log(string $message): void
	{
		file_put_contents($this->logFile, $message . "\n", FILE_APPEND);
	}
}

# Yeni boş dosya
$file = Tester\FileMock::create('');

$logger = new Logger($file);
$logger->log('Login');
$logger->log('Logout');

# Oluşturulan içeriği test et
Assert::same("Login\nLogout\n", file_get_contents($file));

İsteğe bağlı ikinci parametre $extension, üretilen URL'deki dosya uzantısını ayarlar; test edilen kod buna göre karar veriyorsa kullanışlıdır:

$file = Tester\FileMock::create('{"key": "value"}', 'json');

Assert::with()

Bu bir assertion değil, nesnelerin private metotlarını ve özelliklerini test etmeye yarayan bir yardımcıdır.

class Entity
{
	private $enabled;
	// ...
}

$ent = new Entity;

Assert::with($ent, function () {
	Assert::true($this->enabled); // private $ent->enabled erişilebilir
});

Helpers::purge()

purge() metodu belirtilen dizini oluşturur, zaten varsa içeriğinin tamamını siler. Geçici bir dizin oluşturmak için yararlıdır. Örneğin tests/bootstrap.php içinde:

@mkdir(__DIR__ . '/tmp');  # @ - dizin zaten var olabilir

define('TempDir', __DIR__ . '/tmp/' . getmypid());
Tester\Helpers::purge(TempDir);

Environment::lock()

Testler paralel çalışır. Ancak bazen testlerin çalışmasının örtüşmemesi gerekir. Tipik olarak veritabanı testleri, veritabanı içeriğinin hazırlanmasını ve çalışması sırasında başka hiçbir testin veritabanına karışmamasını gerektirir. Bu testlerde Tester\Environment::lock($name, $dir) kullanırız:

Tester\Environment::lock('database', __DIR__ . '/tmp');

İlk parametre kilidin adı, ikincisi kilidin saklanacağı dizinin yoludur. Kilidi ilk alan test devam eder, diğer testler onun tamamlanmasını beklemek zorundadır.

Environment::bypassFinals()

final işaretli sınıfları ya da metotları test etmek zordur. Bir testin başında Tester\Environment::bypassFinals() çağırmak, kod yüklenirken final anahtar sözcüklerinin atlanmasını sağlar.

require __DIR__ . '/bootstrap.php';

Tester\Environment::bypassFinals();

class MyClass extends NormallyFinalClass  # <-- NormallyFinalClass artık final değil
{
	// ...
}

Environment::setup()

  • hata dökümlerinin okunurluğunu artırır (renklendirme dahil); aksi hâlde PHP'nin varsayılan çağrı yığını yazdırılır
  • testte assertion çağrıldığının denetlenmesini sağlar; aksi hâlde assertion'sız testler (örneğin unutulanlar) de geçer
  • çalıştırılan kod hakkında bilgi toplamayı otomatik başlatır (--coverage kullanıldığında) (ayrıntısı aşağıda)
  • betiğin sonunda OK ya da FAILURE durumunu yazdırır

Environment::setupFunctions()

Testlerinizi yapılandırabileceğiniz genel test(), testException(), testNoError(), setUp() ve tearDown() fonksiyonlarını oluşturur.

test('test açıklaması', function () {
	Assert::same(123, foo());
	Assert::false(bar());
	// ...
});

Environment::VariableRunner

Testin doğrudan mı yoksa Tester üzerinden mi çalıştırıldığını belirlemenizi sağlar.

if (getenv(Tester\Environment::VariableRunner)) {
	# Tester tarafından çalıştırıldı
} else {
	# başka bir yolla çalıştırıldı
}

Environment::VariableThread

Tester, testleri belirtilen sayıda iş parçacığında paralel çalıştırır. İş parçacığı numarasıyla ilgileniyorsak, onu ortam değişkeninden buluruz:

echo "Running in thread number " . getenv(Tester\Environment::VariableThread);