İçindekiler
Test yazmanın faydası, test yazmadığınız bir projede bir değişikliğin başka bir yeri bozduğunu canlı ortamda öğrendiğiniz gün anlaşılır. O gün genellikle bir müşteri arar, siz de neyin, ne zaman ve neden bozulduğunu bulmaya çalışırsınız.
Birim test nedir?
Birim test, kodunuzun küçük bir parçasını, genellikle tek bir fonksiyonu ya da sınıfı, dış dünyadan bağımsız olarak çalıştırıp beklenen sonucu verip vermediğini otomatik olarak kontrol eden koddur.
"Dış dünyadan bağımsız" önemli bir ifade. Birim testler veritabanına, ağa ya da dosya sistemine dokunmaz. Bu yüzden saniyenin kesirleri içinde çalışır ve her kayıtta, hatta her değişiklikte çalıştırılabilir.
Ne kazandırır?
1. Korkusuz değişiklik. Eski bir kodu düzenlerken en büyük korku, başka bir yeri bozmaktır. Testler bu korkuyu ortadan kaldırır. Değişikliği yaparsınız, testleri çalıştırırsınız; yeşilse devam edersiniz.
2. Hataların bir daha dönmemesi. Bir hatayı düzeltirken önce o hatayı yeniden üreten bir test yazarsınız. Test kırmızıdır. Hatayı düzeltirsiniz, test yeşile döner. O hata artık bir daha fark edilmeden geri gelemez.
3. Daha iyi tasarım. Test edilmesi zor olan kod, genellikle çok fazla iş yapan ya da çok fazla şeye bağımlı olan koddur. Test yazmak sizi daha küçük ve daha net sorumlulukları olan fonksiyonlar yazmaya iter.
4. Yaşayan belge. Bir fonksiyonun hangi girdide ne döndürdüğünü en net anlatan şey, onun testleridir. Üstelik eski yorum satırlarının aksine testler yanlış bilgi veremez; yanlışsa kırmızı yanar.
PHPUnit ile ilk test
Kurulum:
composer require --dev phpunit/phpunit
Test edeceğimiz basit bir sınıf. KDV dahil fiyat hesaplayan ve indirim uygulayan bir fiyat hesaplayıcı:
<?php
// src/Fiyat.php
final class Fiyat
{
public static function kdvDahil(int $kurus, int $kdvOrani): int
{
if ($kurus < 0) {
throw new InvalidArgumentException('Fiyat negatif olamaz');
}
return (int) round($kurus * (100 + $kdvOrani) / 100);
}
public static function indirimli(int $kurus, int $yuzde): int
{
$yuzde = max(0, min(100, $yuzde));
return (int) round($kurus * (100 - $yuzde) / 100);
}
}
Testi:
<?php
// tests/FiyatTest.php
use PHPUnit\Framework\TestCase;
final class FiyatTest extends TestCase
{
public function testKdvDahilFiyatHesaplanir(): void
{
$this->assertSame(12000, Fiyat::kdvDahil(10000, 20));
}
public function testKurusYuvarlamasiDogruYapilir(): void
{
$this->assertSame(1199, Fiyat::kdvDahil(999, 20)); // 1198,8 → 1199
}
public function testNegatifFiyatHataVerir(): void
{
$this->expectException(InvalidArgumentException::class);
Fiyat::kdvDahil(-1, 20);
}
public function testIndirimYuzdeYuzuGecemez(): void
{
$this->assertSame(0, Fiyat::indirimli(5000, 150));
}
}
Çalıştırmak:
vendor/bin/phpunit tests
.... 4 / 4 (100%)
OK (4 tests, 4 assertions)
Her nokta geçen bir testi gösterir. Bir test başarısız olursa PHPUnit beklenen ve gerçekleşen değeri yan yana gösterir.
İyi bir test nasıl olur?
- Tek bir şeyi test eder ve adı ne test ettiğini söyler.
testKdvDahilFiyatHesaplanir,test1'den çok daha fazla şey anlatır. - Hızlıdır. Yüzlerce birim test birkaç saniyede bitmelidir. Yavaş testler çalıştırılmaz.
- Bağımsızdır. Testlerin çalışma sırası sonucu değiştirmemelidir.
- Kenar durumları kapsar. Sıfır, negatif sayılar, boş dizi, çok büyük değerler, Türkçe karakterler. Hataların büyük kısmı bu kenarlarda yaşanır.
- Uygulamayı değil davranışı test eder. Fonksiyonun içinde hangi adımların atıldığını değil, ne döndürdüğünü kontrol edin. Böylece kodu yeniden düzenlediğinizde testler gereksiz yere kırılmaz.
Neyi test etmeli?
Her şeyi test etmek zorunda değilsiniz. Öncelik sırası:
- Para, tarih ve hesaplama içeren iş kuralları: Fiyat, vergi, indirim, kargo ücreti, abonelik yenileme tarihi. Buradaki bir hata doğrudan para kaybıdır.
- Daha önce hata çıkmış yerler. Her düzeltilen hata için bir test.
- Doğrulama kuralları: T.C. kimlik numarası, IBAN, telefon, e-posta kontrolleri.
- Karmaşık koşullar içeren fonksiyonlar. Çok sayıda
ifveelseiçeren her yer.
Basit getter ve setter metotlarını ya da çatının (framework) kendi işlevlerini test etmek ise genellikle zaman kaybıdır.
Veritabanı ve dış servisler ne olacak?
Kodunuz bir ödeme servisine ya da veritabanına bağımlıysa, birim testte bu bağımlılığın yerine sahte bir nesne (mock ya da stub) kullanılır. Bu nesne gerçek servis gibi davranır ama ağa çıkmaz. Gerçek veritabanı ve servislerle birlikte çalışmayı test etmek ise entegrasyon testlerinin işidir. İkisi birbirini tamamlar.
Küçük başlayın
Mevcut bir projeye bir anda yüzlerce test yazmanız gerekmiyor. Bir sonraki hatayı düzeltirken bir test yazın. Bir sonraki yeni fonksiyonu yazarken iki test yazın. Testleri CI sürecinize ekleyin ki her commit'te otomatik çalışsın. Birkaç ay içinde, projenin en kritik kısımlarının güvence altında olduğunu ve değişiklik yapmaktan artık korkmadığınızı fark edeceksiniz.
Yorumlar
Henüz yorum yok. İlk yorumu siz yazın.