Merhabalar arkdaşlar.

Bu yazıda TDD (Test Driven Development) kavramının ne olduğuna değineceğiz ve Java dili ile TDD kavramını örneklendirmeye çalışacağız.

TDD Nedir?

Açılımı Test Driven Development olan ve Türkçe’ye Test Güdümlü Development olarak çevirebileceğimiz, ilk defa Kent Beck yazılım üstadı tarafından ortaya atılmış olan TDD aslında tüm yazılım geliştiricilerin uygulaması gereken bir kavram. TDD ana mantık olarak bir kod geliştirilirken kodu yazmadan önce o kodun yapması gereken işin testinin yazılması ve kodun yazılan bu testten geçecek şekilde yazılmasıdır. Yani diyelim ki bir yazılım geliştirici veri tabanına kayıt ekleyen bir modül yazacak.

  • Önce gidip bu modulün nasıl olması gerektiğine göre testini yazıyor.
  • Testi çalıştırıp fail’ini yani kırmızı rengi alıyor.
  • Sonra modülü testdeki şartları sağlayabilecek yani testi geçebilecek şekilde yazıyor.
  • Son olarak test metodunu koşturuyor ve ilgili kodun testi geçtiğini yani yeşil rengi görüyor.

Test deyince hepimizin aklında üretilmiş, bitirilmiş bir yazılımın test edilmesi gibi bir düşünce belirebilir. Fakat TDD kavramı ile bu düşünce tamamen farklı olaylar. TDD yazılım geliştirilirken test güdümlü olunmasını gerektirir. Yazılım bittikten sonra varsa ilgili şirketin test ekibi, yoksa danışman bir şirket vasıtası ile yazılımın testleri yapılabilir.

Şimdi bu TDD kavramını Java dili ve JUnit kütüphanesini kullanarak örnekleyelim. Basit olacak bu örneğimizde toplama çıkarma modulünü TDD yaklaşımı ile yazacağız.

Kullandığınız IDE’de TDDWithJava adında bir Maven projesi oluşturalım arkadaşlar. Ben bu yazı için Eclipse IDE kullanıyor olacağım. Projenin açılması akabinde pom.xml dosyamıza şu bağımlılığı eklemek sureti ile JUnit kütüphanesinin bu yazı yazılırkenki son sürümünü projemize ekleyelim.

<dependencies>
	<!-- https://mvnrepository.com/artifact/junit/junit -->
	<dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
	</dependency>
</dependencies> 

Ardından src/main/java altında interfaces adında bir package oluşturalım ve Adder ile Subractor interface’lerini ekleyelim. Interface’lerin kodlar şöyle:

Adder.java

package com.ilkaygunel.interfaces;

public interface Adder {
	public long add(long... operands);
}

Subtractor.java

package com.ilkaygunel.interfaces;

public interface Subtractor {
	public long subtract(long... operands);
}

Şimdi de implementation adında bir package oluşturalım ve yukarıdaki interface’lerin implemente edildiği classları oluşturalım. Eclipse IDE’ye interfacele’ri implement ettirdiğimizde default olarak dönüş değeri 0 olan metotlar yazılır.

package com.ilkaygunel.implementation;

import com.ilkaygunel.interfaces.Adder;
import com.ilkaygunel.interfaces.Subtractor;

public class Calculator implements Adder,Subtractor{

	public long subtract(long... operands) {
		// TODO Auto-generated method stub
		return 0;
	}

	public long add(long... operands) {
		// TODO Auto-generated method stub
		return 0;
	}
}

Şimdi src/test/java altında tests adında bir package oluşturalım ve package’a sağ tıklayıp New -> JUnit Test Case diyelim.

Şimdi gelen ekranda New JUnit 4 Test kısmının işaretli olduğundan emin olalım. Name kısmına CalculatorTest ismini yazalım. setUp() metoduna tik koyalım. setUp() metoduna tik koymamız @Before notasyonu ile işaretli bir metodu Test Sınıfımıza dahil edecek. @Before notasyonu da test koşulmadan önce gerçekleştirilecek durumları belirtmek içindir. Son olarak da en alt kısımdaki Class Under Test kısmında bizim test edeceğimiz sınıf olan Calculator’ı seçelim. Next butonuna tıklayalım.

Şimdi gelen ekranda ise hangi metotlar için test yazmak istediğimizi seçmemiz isteniyor. Biz de add ve subtract metotlarını işaretliyoruz. Finish butonuna tıklıyoruz.

Şu şekilde bir kod gelicek:

package com.ilkaygunel.tests;

import static org.junit.Assert.fail;

import org.junit.Before;
import org.junit.Test;

public class CalculatorTest {

	@Before
	public void setUp() throws Exception {
	}

	@Test
	public void testSubtract() {
		fail("Not yet implemented");
	}

	@Test
	public void testAdd() {
		fail("Not yet implemented");
	}
}

Şimdi bu sınıfa sağ tıklayıp Rus As -> JUnit Test diyelim. @Test notasyonu ile işaretli metotlar içindeki fail() metotları nedeni ile testlerimiz fail olacaktır.

Şimdi TDD mantığını uygulayacağımız yere geldik. Öncelikle Calculator içindeki add ve subtract metotlarını test eden metotlarımızın içeriğini beklentilerimize göre değiştirelim. Bizim beklentimiz toplama ve çıkarma yapan metotların doğru sonuçlar vermesidir.

Test sınıfımızı şöyle düzenleyelim:

package com.ilkaygunel.tests;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import com.ilkaygunel.implementation.Calculator;

public class CalculatorTest {

	private Calculator calculator;

	@Before
	public void setUp() throws Exception {
		calculator = new Calculator();
	}

	@Test
	public void testSubtract() {
		long result = 3 - 11 - (-5) - 22;

		Assert.assertEquals(result, calculator.subtract(3, 11, -5, 22));
	}

	@Test
	public void testAdd() {
		long result = 3 + 11 + (-5) + 22;

		Assert.assertEquals(result, calculator.add(3, 11, (-5), 22));
	}
}

JUnit ile koşturulan test metotlarında her @Test notasyonu ile işaretli metot kendi alanları içinde koşarlar. Yani sınıf içerisinde tanımlı calculator nesnesi @Before notasyonu vasıtası ile her test için ayrı ayrı newlenecek ve her testte de bu ayrı nesne kullanılacak.

testSubtract ve testAdd metotları içinde kendimiz birer result üretiyoruz. Akabinde JUnit kütüphanesi içinde yer alan Assert sınıfında yer alan, iki değerin eşitliğini kıyaslayan assertEquals metodu ile ürettiğimiz result değişkeni ve Calculator sınıfımızdaki metotların ürettiği değerleri kıyaslıyoruz.

Şimdi tekrar sağ tıklayıp Rus As -> JUnit Test diyelim.

Test kodlarımız gördüğünüz gibi yine fail oldu çünkü Calculator sınıfımız içindeki metotlarımız 0 döndürüyor. Dolayısı ile biz de fail alıyoruz. Şimdi sıra implementasyon sınıfımızı Test sınıfımıza göre yazmaya ve testimizi geçecek hale getirmeye geldi.

add ve subtract metotlarınızı şu şekilde toplama ve çıkarma yapıp sonucu döndürecek şekilde güncelleyelim:

package com.ilkaygunel.implementation;

import com.ilkaygunel.interfaces.Adder;
import com.ilkaygunel.interfaces.Subtractor;

public class Calculator implements Adder, Subtractor {

	public long subtract(long... operands) {
		long result = operands[0];
		
		for (int index = 1; index < operands.length; index++) {
			result -= operands[index];
		}

		return result;
	}

	public long add(long... operands) {
		long result = 0;
		
		for (long number : operands) {
			result += number;
		}
		
		return result;
	}
}

Test sınıfımızı tekrar çalıştırdığımızda testlerin success aldığını ve yeşil rengi göreceğiz.

Bu yazıda basitçe TDD mantığını anlatmaya ve bir örnekle durumu somutlaştırmaya çalıştım arkadaşlar. Başka bir yazıda görüşene kadar sağlıcakla kalın.

Selam ve Sevgilerimle

NOT: Yararlanılan ve okunması faydalı linkler:

http://devnot.com/2015/tdd-test-driven-development-gercekten-zor-mu/ http://www.cihataltuntas.com/test-driven-development/ http://www.alikoprulu.com.tr/test-driven-development-tdd-nedir/ http://www.cangelis.com/test-driven-development-tdd-nedir/ https://www.youtube.com/watch?v=2Ekty7t621k