Yazma Testleri

Nette Tester için test yazmak, her testin bağımsız olarak çalıştırılabilen bir PHP betiği olması açısından benzersizdir. Bu büyük bir potansiyele sahiptir. Testi yazarken, düzgün çalışıp çalışmadığını görmek için basitçe çalıştırabilirsiniz. Çalışmazsa, IDE'de kolayca adım atabilir ve bir hata arayabilirsiniz.

Testi bir tarayıcıda bile açabilirsiniz. Ancak her şeyden önce – çalıştırarak testi gerçekleştireceksiniz. Geçip geçmediğini veya başarısız olup olmadığını hemen öğreneceksiniz.

Giriş bölümünde, PHP dizilerini kullanmanın gerçekten önemsiz bir testini gösterdik. Şimdi de basit olmasına rağmen test edeceğimiz kendi sınıfımızı oluşturacağız.

Bir kütüphane ya da proje için tipik bir dizin düzeni ile başlayalım. Testleri kodun geri kalanından ayırmak önemlidir, örneğin dağıtım nedeniyle, çünkü testleri sunucuya yüklemek istemiyoruz. Yapı aşağıdaki gibi olabilir:

├── src/           # code that we will test
│   ├── Rectangle.php
│   └── ...
├── tests/         # tests
│   ├── bootstrap.php
│   ├── RectangleTest.php
│   └── ...
├── vendor/
└── composer.json

Ve şimdi tek tek dosyalar oluşturacağız. Test edilen sınıf ile başlayacağız ve bunu şu dosyaya yerleştireceğiz src/Rectangle.php

<?php
class Rectangle
{
	private float $width;
	private float $height;

	public function __construct(float $width, float $height)
	{
		if ($width < 0 || $height < 0) {
			throw new InvalidArgumentException('The dimension must not be negative.');
		}
		$this->width = $width;
		$this->height = $height;
	}

	public function getArea(): float
	{
		return $this->width * $this->height;
	}

	public function isSquare(): bool
	{
		return $this->width === $this->height;
	}
}

Ve bunun için bir test oluşturacağız. Test dosyasının adı *Test.php veya *.phpt maskesiyle eşleşmelidir, biz RectangleTest.php varyantını seçeceğiz:

<?php
use Tester\Assert;

require __DIR__ . '/bootstrap.php';

// genel dikdörtgen
$rect = new Rectangle(10, 20);
Assert::same(200.0, $rect->getArea()); # beklenen sonuçları doğrulayacağız
Assert::false($rect->isSquare());

Gördüğünüz gibi, Assert::same() gibi onaylama yöntemleri, gerçek bir değerin beklenen bir değerle eşleştiğini onaylamak için kullanılır.

Son adım bootstrap.php dosyasını oluşturmaktır. Tüm testler için ortak bir kod içerir. Örneğin sınıfların otomatik yüklenmesi, ortam yapılandırması, geçici dizin oluşturma, yardımcılar ve benzerleri. Her test bootstrap'ı yükler ve sadece test etmeye dikkat eder. Bootstrap aşağıdaki gibi görünebilir:

<?php
require __DIR__ . '/vendor/autoload.php'; # Composer otomatik yükleyicisini yükle

Tester\Environment::setup(); # Nette Tester'ın başlatılması

// ve diğer konfigürasyonlar (sadece bir örnek, bizim durumumuzda bunlara gerek yoktur)
date_default_timezone_set('Europe/Prague');
define('TmpDir', '/tmp/app-tests');

Bu bootstrap, Composer otomatik yükleyicisinin Rectangle.php sınıfını da yükleyebileceğini varsayar. Bu, örneğin composer.json adresindeki autoload bölümünü ayarlayarak vb. gerçekleştirilebilir.

Artık testi komut satırından diğer bağımsız PHP betikleri gibi çalıştırabiliriz. İlk çalıştırma herhangi bir sözdizimi hatasını ortaya çıkaracaktır ve eğer bir yazım hatası yapmadıysanız, göreceksiniz:

$ php RectangleTest.php

OK

Testte ifadeyi yanlış olarak değiştirirsek Assert::same(123, $rect->getArea());, bu gerçekleşecektir:

$ php RectangleTest.php

Failed: 200.0 should be 123

in RectangleTest.php(5) Assert::same(123, $rect->getArea());

FAILURE

Test yazarken, tüm uç durumları yakalamak iyidir. Örneğin, giriş sıfırsa, negatif bir sayı ise, diğer durumlarda boş bir dize, null vb. Aslında, sizi bu tür durumlarda kodun nasıl davranması gerektiğini düşünmeye ve karar vermeye zorlar. Testler daha sonra davranışı düzeltir.

Bizim durumumuzda, negatif bir değer Assert::exception() ile doğruladığımız bir istisna oluşturmalıdır:

// genişlik negatif sayı olmamalıdır
Assert::exception(
	fn() => new Rectangle(-1, 20),
	InvalidArgumentException::class,
	'Boyut negatif olmamalıdır.',
);

Boy için de benzer bir test ekliyoruz. Son olarak, her iki boyut da aynıysa isSquare() 'un true döndürdüğünü test ediyoruz. Bu tür testleri alıştırma olarak yazmaya çalışın.

İyi Düzenlenmiş Testler

Test dosyasının boyutu artabilir ve hızla dağınık hale gelebilir. Bu nedenle, test edilen alanları ayrı işlevler halinde gruplamak pratiktir.

İlk olarak, test() global fonksiyonunu kullanarak daha basit ama zarif bir varyant göstereceğiz. Kodunuzda aynı isimde bir fonksiyon varsa çakışmayı önlemek için test cihazı bunu otomatik olarak oluşturmaz. Yalnızca bootstrap.php dosyasında çağırdığınız setupFunctions() yöntemi tarafından oluşturulur:

Tester\Environment::setup();
Tester\Environment::setupFunctions();

Bu fonksiyonu kullanarak, test dosyasını güzel bir şekilde adlandırılmış birimlere bölebiliriz. Çalıştırıldığında, etiketler birbiri ardına görüntülenecektir.

<?php
use Tester\Assert;

require __DIR__ . '/bootstrap.php';

test('general oblong', function () {
	$rect = new Rectangle(10, 20);
	Assert::same(200.0, $rect->getArea());
	Assert::false($rect->isSquare());
});

test('general square', function () {
	$rect = new Rectangle(5, 5);
	Assert::same(25.0, $rect->getArea());
	Assert::true($rect->isSquare());
});

test('dimensions must not be negative', function () {
	Assert::exception(
		fn() => new Rectangle(-1, 20),
        InvalidArgumentException::class,
	);

	Assert::exception(
		fn() => new Rectangle(10, -1),
        InvalidArgumentException::class,
	);
});

Kodu her testten önce veya sonra çalıştırmanız gerekiyorsa, setUp() veya tearDown() adresine iletin:

setUp(function () {
	// her test() öncesinde çalıştırılacak başlatma kodu
});

İkinci varyant nesnedir. Tek tek birimlerin adları test- ile başlayan yöntemlerle temsil edildiği bir sınıf olan TestCase'i oluşturacağız.

class RectangleTest extends Tester\TestCase
{
	public function testGeneralOblong()
	{
		$rect = new Rectangle(10, 20);
		Assert::same(200.0, $rect->getArea());
		Assert::false($rect->isSquare());
	}

	public function testGeneralSquare()
	{
		$rect = new Rectangle(5, 5);
		Assert::same(25.0, $rect->getArea());
		Assert::true($rect->isSquare());
	}

	/** @throws InvalidArgumentException */
	public function testWidthMustNotBeNegative()
	{
		$rect = new Rectangle(-1, 20);
	}

	/** @throws InvalidArgumentException */
	public function testHeightMustNotBeNegative()
	{
		$rect = new Rectangle(10, -1);
	}
}

// Test yöntemlerini çalıştırın
(new RectangleTest)->run();

Bu sefer istisnaları test etmek için @throw ek açıklamasını kullandık. Daha fazla bilgi için TestCase bölümüne bakın.

Yardımcı Fonksiyonlar

Nette Tester, testi sizin için kolaylaştırabilecek çeşitli sınıflar ve işlevler içerir; örneğin, bir HTML belgesinin içeriğini test etmek için yardımcılar, dosyalarla çalışma işlevlerini test etmek vb.

Bunların açıklamasını Yardımcılar sayfasında bulabilirsiniz.

Ek Açıklama ve Atlama Testleri

Test yürütmesi, dosyanın başındaki phpDoc yorumundaki ek açıklamalardan etkilenebilir. Örneğin, şöyle görünebilir:

/**
 * @phpExtension pdo, pdo_pgsql
 * @phpVersion >= 7.2
 */

Ek açıklamalar, testin yalnızca PHP sürüm 7.2 veya üstü ile ve PHP uzantıları pdo ve pdo_pgsql mevcutsa çalıştırılması gerektiğini söyler. Bu ek açıklamalar, koşullar karşılanmazsa testi atlayan ve s harfi ile işaretleyen komut satırı test çalıştırıcısı tarafından kontrol edilir – atlandı. Ancak, test manuel olarak çalıştırıldığında hiçbir etkisi yoktur.

Ek açıklamaların açıklaması için Test Ek Açıklamaları bölümüne bakın.

Test ayrıca Environment::skip() ile kendi koşuluna göre de atlanabilir. Örneğin, Windows üzerinde bu testi atlayacağız:

if (defined('PHP_WINDOWS_VERSION_BUILD')) {
	Tester\Environment::skip('Requires UNIX.');
}

Dizin Yapısı

Sadece biraz daha büyük kütüphaneler veya projeler için, test dizinini test edilen sınıfın ad alanına göre alt dizinlere bölmenizi öneririz:

└── tests/
	├── NamespaceOne/
	│   ├── MyClass.getUsers.phpt
	│   ├── MyClass.setUsers.phpt
	│   └── ...
	│
	├── NamespaceTwo/
	│   ├── MyClass.creating.phpt
	│   ├── MyClass.dropping.phpt
	│   └── ...
	│
	├── bootstrap.php
	└── ...

Testleri tek bir isim alanından yani alt dizinden çalıştırabileceksiniz:

tester tests/NamespaceOne

Uç Vakalar

Herhangi bir assertion yöntemini çağırmayan bir test şüphelidir ve hatalı olarak değerlendirilecektir:

Error: This test forgets to execute an assertion.

İddiaları çağırmadan yapılan test gerçekten geçerli kabul edilecekse, örneğin Assert::true(true) adresini çağırın.

Testi bir hata mesajıyla sonlandırmak için exit() ve die() adreslerini kullanmak da tehlikeli olabilir. Örneğin, exit('Error in connection') testi başarıya işaret eden 0 çıkış koduyla sonlandırır. Assert::fail('Error in connection') kullanın.