Murat Çileli

PHP'de Birim Testi (Unit Testing)

Aralarına benim de dahil olduğum bir çok yazılımcının, kullanıcılardan en sık duyduğu cümle "Bir tarafı yaparken diğer tarafı bozuyorsun" olsa gerek. Aslında bu da her ne kadar literatürde Cowboy Coding olarak geçen bir yazılım geliştirme disiplini olsa da, "olması gereken" bir yaklaşım değil.

Demek ki, bir yazılım son kullanıcıya gitmeden önce test edilmeli. İşte bu noktada Birim Testi (Unit Testing) kavramı karşımıza çıkıyor.

Neden Birim Testi?

Birim Testi yöntemine göre "birim" sizin en küçük kod parçanızı temsil eder. Bu bir fonksiyon olabildiği gibi bir sınıfın metodu da olabilir. Bir yazılımın tüm "birim"leri doğru çalışıyorsa, yazılım doğru çalışıyor demektir. Birim Testi'nin temel mantığı, parçadan bütüne doğru gitmek…

Biraz daha anlayabilmek için şöyle bir örnek verelim. Bir otomobilin bütün parçalarının doğru çalıştığından eminsek, otomobilin doğru çalıştığından da emin olabiliriz. Aksi yaklaşımda siz otomobilinizin bütününe bakarak sorunsuz olduğunu düşünebilirsiniz ancak gerçekte hız göstergesi doğru çalışmıyor olabilir ya da göremediğiniz "birim"lerde hatalar olabilir.

Diğer bir yaklaşımla Birim Testi, makine tarafından yorumlanan kod parçalarının yine makine tarafından kontrol edilmesi prensibidir.

PHPUnit

Birim Testi, programlama dillerinden veya platformlardan bağımsız bir kavramdır. JAVA'da, Delphi'de, C#.Net'te ve diğer tüm programlama dillerinde uygulanabilir. Bu yazıda PHP ile Birim Testi kavramına "kuşbaşı" bakacacağız çünkü bu hususta gerekli bilgiler esasen PHPUnit dokümantasyonunda verilmiş.

PHPUnit, PHP'de Birim Testi için kullanılan kütüphanelerden en bilineni ve en kapsamlısı. PHPUnit'i bilgisayarımdaki WAMP Server'a kurmaya çalıştıysam da, hayatın Windows'a PEAR pakedi kurmakla uğraşacak kadar uzun olmadığını olduğunu hatırlayıp Digital Ocean‘daki droplet'ime kurmaya karar verdim.

Ne demiştik?… Birim Testi, birimlerimizi test eder. Öyleyse ilk birimimizi test edelim.

class OrnekTest extends PHPUnit_Framework_TestCase
{
    public function testBir()
    {
        $this->assertTrue(FALSE);
    }
}

Test sınıflarımızın PHPUnit_Framework_TestCase sınıfından türemesi gerektiğini hatırlatalım ve konsolumuzdan bu dosyayı çalıştıralım.

phpunit --verbose ornek1.php

Çıktımız şu şekilde olacak.

There was 1 failure:

1) OrnekTest::TestBir
Failing asserting that false is true.

Testimiz başarısız oldu! Bunun sebebi, biz assertTrue ile "TRUE" yani doğru olması gereken bir sonuç bekliyoruz ancak test ettiğimiz birim "FALSE" yani yanlışın ta kendisi. Şimdi kodumuzu aşağıdaki şekilde düzenleyelim.

class OrnekTest extends PHPUnit_Framework_TestCase
{
    public function testBir()
    {
        $this->assertTrue(TRUE);
    }
}
... ve tekrar çalıştıralım.
OK (1 test, 1 assertion)

Testimiz bu sefer başarılı oldu. Çünkü sonucu TRUE olması beklenen bir fonksiyona TRUE gönderdik.

Konuyu daha iyi anlayabilmek için daha gerçekçi bir örnekle devam edelim. assertTrue fonksiyonu "Boolean" tipinde dönüşü olan metodları test etmek için kullanılır. Bunun gibi bir diğer metod ise assertEqualsolup, dönen sonucun beklenene eşit olup olmadığını kontrol eder.

class OrnekTest extends PHPUnit_Framework_TestCase
{
    public function testIki()
    {
	$sonuc = 2 + 2;
        $this->assertEquals(4, $sonuc);
    }
}

Bu örnekte assertEquals fonksiyonunun ilk parametresi "olması beklenen" değer, ikinci parametresi ise bizim gönderdiğimiz değer. Tahmin edeceğiniz gibi bu kod da aşağıdaki gibi Birim Testi'ni başarıyla geçecektir.

Peki projemizin mevcut kodlarını Birim Testi'ne nasıl dahil edeceğiz? Örneğin elimizde KDV hesaplaması için yazdığımız şöyle bir sınıf olsun.

class KDV_Hesapla
{
    private $tutar;
    public $kdv_dahil_tutar;

    public function __construct($tutar)
    {
        $this->tutar = $tutar;
    }

    public function kdv_hesapla()
    {
        return $this->kdv_dahil_tutar = $this->tutar * 1.18;
    }
}

Yazılımımızın KDV hesaplayan birimini test etmek için şöyle bir Birim Testi sınıfı yazıyoruz.

class KDVHesaplaTest extends PHPUnit_Framework_TestCase
{
    public function KDVSonucuDogruMu()
    {
	$kdv = new KDV_Hesapla(50);
        $this->assertEquals(59, $kdv->kdv_dahil_tutar());
    }
}

Birim Testi'mize ilk önce sınıfımızın olduğu dosyayı dahil ettik. Sonra sınıfımızdan yeni bir nesne oluşturarak 50 TL'lik tutarın "eşit olması gereken" sonuca yani 59 TL'ye eşit olup olmadığını kontrol ettik. Bu testimiz de başarıyla sonuçlanacak.

OK (1 test, 1 assertion)

Projemizdeki her birim için bir test yazılmalı, birimlerin sınıf isimleri ve fonksiyon isimleri yapılan işi anlatacak kadar açıklayıcı olmalı.

Birim Testi, gün geçtikçe karmaşıklaşan yazılım geliştirme ve yazılım testi sürecinde önemli bir konu. Önce testlerin yazıldığı ve tüm testler "başarılı" olana kadar yazılımın kodlandığı Test-driven Development disiplinini araştırmanızı öneririm.