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.