TDD ve BDD üzerine

3 yıldır TDD uyguluyoruz ve yakın zamanda BDD ile proje yapma fırsatı bulduk. TDD’yi J.B. Rainsberger’dan (kendisini çok sevdiğimiz Joe’dan) öğrendim, BDD’yi de daha sonra kendi çabamla öğrenip, deneyip ekibime anlattım.

İşe alım görüşmelerimde ve etrafımdaki yazılımcı arkadaşlarda gördüğüm fikir karmaşasına biraz daha açıklık getirmek üzere bu yazıyı hem yazılımcı hem yönetici penceresinden bakan bir açıyla yazma ihtiyacı hissettim.

Öncelikle şunu söylemeliyim ki; TDD, BDD, RDD, DDD ve yakın zamanda MDD ortaya çıkmadan da ekibimle gayet güzel iş yapıp teslim ediyorduk. Bug da çıkıyordu, requirement eksikleri de sonradan ortaya çıktığında dizayn bozukluğuna sebep oluyor ve patchy kod yazmamızı gerektiriyordu. Yazdığımız projeler yine 3 yıl sonra düzgün yönetilse bile (projesine göre değişir) ek yapıldıkça yavaş yavaş tekrar yazılma ihtiyacı gerektiren yöne doğru gidiyordu.

Yukarıdakileri kötü kod yazılıyor şeklinde yorumlamayın sakın, standartlarımıza bağlı, üzerinden düzenli codewalk yapılan, “temiz” kodlar yazdığımızdan emindik. Etrafımda çalışan arkadaşlar çok iyi yazılım mühendisleriydi.

TDD ile yazmaya başladığımızda hayatımızda ne değişti? Öncelike öğrenme sürecinde ciddi vakit kaybı yaşadık. Kafasında modeller tasarlayıp hemen uygulamaya geçen yazılımcıları dizginleyip, onlara işin nereye gideceğini bildikleri halde kafalarındaki bütünü bir yere not alıp, sonra tek tek, parça parça, ufaktan başlayıp test ederek o bütüne, testler başka yöne götürürse de o düzgün çalışan başka bütüne ulaşmayı öğrettik. Özellikle red-green-refactor yaparken refactor kısmında ne kadar iyi mühendislik, uzmanlık ve pattern bilgisi işin içine girerse o kadar düzgün bir yapıya ulaşılabileceğini gördük.

Öncelikle bize ters gelen bu yapı daha sonra alışkanlık haline geldi. Bu vaktin ve proje sürelerinin uzamasının bize nasıl bir dönüşü oldu derseniz en önemlileri;

– Artık kodun hiçbir parçasını değiştirmekten üşenmiyor, korkmuyoruz
– Deployment yapmaktan korkmuyoruz
– Proje bittiği noktadan itibaren yeni eklemeleri çok rahat yapabiliyoruz

Tabi yan getirileri de her şirketin (özellikle turnover yüksek olan şirketlerin) korktuğu, “bu kodun sahibi giderse ne yaparız”, “kod dokümanımız yeterli mi” sorularının cevabı olmuştu TDD. Testler, eğer düzgün isim verilirse kodun ne için yazıldığının dokümanı oluyor. Pair programming mantığı ise her adamın sürekli o koda ve yapıya alışık dublörüne şirket içinde acil durumlarda ulaşılabilir olmasını sağlıyor.

Fakat TDD’nin çözmediği, daha doğrusu çözmeyi hedeflemediği şeyler var. Koda aceleyle yeni gelen istekleri ekleyip mimari tasarımı hala bozabiliyorsunuz. Bu durumda projeleriniz yine değişme hızına göre 3-4 yıl içinde içine kod yazılmaz hale gelebilir. Yine requirement eksikleri oluşturduğunuz mimari tasarımdan başka birine kaymanızı gerektirebilir.

TDD’nin hedeflemediği asıl şey şirket ve yazılımcı kültürü, prensipleri. Eğer bu ikisinin prensipleri ve yaklaşımları yazılımın bakım ve ek istek uygulandığı aşamaları da düzgün yönetecek, bu aşamalarda da sadece ek yapılmayıp arada ciddi projeler çıkabileceği yönünde ise o zaman projeleriniz daha ayakları yere basar, olgun ilerleyebiliyor. Mimari tasarımaların değişmesi gerektiği noktada TDD’den sapmamayı kurum kültürü olarak benimseyebiliyorsanız o zaman başarılı olabiliyorsunuz.

Bu başarının tek yolu düzgün iç eğitimden ve yazılımcıların kendini ve projeyi rahat geliştirebilecekleri bir ortamdan geçiyor. Projelerde zaman-maliyet-kalite üçgeni dahilinde en optimumu yakalamak ve piyasa şartlarını kaçırmadan yazılımı teslim etmek için yazılımcılarınızın 10 kaplan gücünde olup altyapınızın ormandan daha düzenli olması gerekiyor.

Artık TDD yaptığımız ve yapmadığımız projelerin her dönem oluşan issue sayısını karşılaştırıp, başta harcadığımız vaktin kurumun itibarına nasıl yansıdığını görebiliyoruz.

BDD ise TDD’deki standardizasyondan türemiş daha sonuç odaklı bir yöntemdir. TDD yapan abilerimiz bir süre sonra sürekli AAA (Arrange-Act-Assert) veya Context-When-Then yaptıklarını görüp daha üst düzeyde bunu refactor ettiklerinde ve üzerine düzgün bir yapı yerleştirdiklerinde yaptıkları testlerin sistem davranışlarını test etmek olduğunu görmüşler. İşin içine iş ve ürün ekiplerini de katmanın ne kadar avantajlı olduğunu ve bunun üzerinden direkt unit-testlere geçilmesinin hız kazandıracağını görünce BDD, TDD gibi kuvvetli bir alternatif haline gelmiş.

BDD’de kullandığımız dil, en azından iş ekipleri açısından biraz ön çalışma gerektirir. Malesef her iş ekibi bu şekilde requirement yazmaya alışkın değildir. Bu yapının içinde olan analistler iş ekipleri ile yazılım ekipleri arasındaki köprüyü oluşturmaktadır. Eğer şirketinizde analist çalıştırmıyorsanız o zaman Behavior testlerini yazılımcıya (hani analyst-developer yazıyor ya kartvizitlerinizde) yazdırıp iş ekiplerine onaylatır, onları da bu yöntemle iş istemeye alıştırabilirsiniz.

BDD testleri yazılımcı tarafından yazıldığında TDD testlerine daha yakın, daha low level bir dil içerir. Sistemin daha alt birimlerini, unit’leri test etmeye daha yatkındır. StoryQ sitesinden alıntı yaptığım alttaki test iş birimi testlerine örnektir.

Story is Data Safety
In order to Keep my data safe
As a User
I want All credit card numbers to be encrypted
With scenario submitting shopping cart
Given I have typed my credit card number into the checkout page
When I click the Buy button                                     
And the browser posts my credit card number over the internet
Then the form should be posted over https

Aşağıdaki testler de bizim projemizden yazılımcının BDD testine örnek;

Story Add Customer To Advertisement Group Display
In order to Display
As an Admin User
I want To Display Customer Advertisment Group
With scenario Display Customer Advertisement Group befora add
Given Admin user loggedin
When AddCustomerToAdvertisementGroup called
And Querystring has CustomerID
And Method is get 
Then ViewName Should Be "Display"
And CustomerDisplayModel should be loaded

Farkındaysanız daha fazla teknik detay teste girmiş durumda. Eğer TDD ile geliştirme yapıyorsanız ve yöntemi doğru uyguluyorsanız, kodunuzun Test Coverage’ı hep 100% olacaktır. Hiç kontrol etmenize gerek yok. Fakat BDD ile test yazmak çok iyi ürün ve yazılım bilgisi gerektirir. Müşterinin hangi ekranda hangi model ile görüntüleceğini teknik olarak bilmeniz (veya test yazım aşamasında aşağı yukarı tahmin etmeniz) gerektiği gibi hangi bilgileri göstermeniz gerektiğini iş mantığı açısından bilmeniz gerekmektedir.

TDD ve BDD’de ortak olarak proje üzerinde karşınıza çıkan bug ve issue’ları da bunlarla ilgili yeni testler yazarak çözmelisiniz. Öncelikle eklemeyi yapıp sonra testleri tamamlamak yanlış olacaktır. Bu şekilde yöntemden uzaklaşmadan devam edebilirsiniz.

Başlarken farkında değildim fakat TDD ve BDD’nin avantajları, hangi kurumlar ve hangi tip ekip profilleri için olduğunu anlatmaya çalıştım. Bu yöntemlerden birine geçmek istiyorsanız ilgili testleri yazacak ve işin genelini yönetecek arkadaşları dikkatli seçmelisiniz.

Gelen soru ve isteklere göre yazının ikinci bölümünü yazmayı düşünebilirim.
Sevgiler, saygılar

.NET Assembly’lerini tek dosya içine gömmek

Merhaba,

Uzun zamandır blog’uma ek yapmaya vaktim olmamış…

Bugün bir ihtiyaç doğdu ve bir çok .dll dosyasına referans veren bir projeyi tek bir .exe dosyası kullanarak çalıştırmam/paylaşmam gerekti. Biraz araştırma yaptım ve aşağıdaki örneği buldum. Benim projemde düzgün çalıştı.

Siz de Windows Forms uygulamalarınızı veya kütüphane projelerinizi tek bir assembly veya tek bir .exe dosyası ile deploy etmek istiyorsanız aşağıdaki adımları izleyebilirsiniz.

  1. Projemize bir resource dosyası ekliyoruz.
  2. Resource dosyasının özelliklerine girip Build Action:Embedded Resource olarak kullanıldığından emin oluyoruz.
  3. Resource dosyamızın içine istediğimiz dll’leri ekliyoruz.
  4. Aşağıdaki örnekteki gibi programın girişine ResourceAssembly ile ilgili yeşil renk ile yazdığım iki satır kodu ekliyoruz. Burada DefaultNamespaceAdi ve ResourceAdi kısımlarınızı kendi projenizdeki isimlerle değiştirmeyi unutmayın.

[STAThread]
static void Main()
{
            ResourceAssemblyLoader.ResourceName = “DefaultNamespaceAdi.ResourceAdi”;
            ResourceAssemblyLoader.LoadAssemblies();

Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmMain());
}
}

5. Alltaki sınıfı projenize ekleyin.

public class ResourceAssemblyLoader
{
static Dictionary libs = new Dictionary();
public static string ResourceName;

public static void LoadAssemblies()
{
AppDomain.CurrentDomain.AssemblyResolve += FindAssem;
}

static Assembly FindAssem(object sender, ResolveEventArgs args)
{
Assembly result;

string dllName = args.Name.Contains(‘,’) ? args.Name.Substring(0, args.Name.IndexOf(‘,’)) : args.Name.Replace(“.dll”, “”);
if (libs.ContainsKey(dllName))
{
result = libs[dllName];
}
else
{
dllName = dllName.Replace(“.”, “_”);
if (dllName.EndsWith(“_resources”)) return null;
ResourceManager rm = new ResourceManager(ResourceName, Assembly.GetExecutingAssembly());
byte[] bytes = (byte[])rm.GetObject(dllName);

result = Assembly.Load(bytes);
libs[dllName] = result;
}

return result;
}

6. Artık bin dizini altında debug ve ya release bölümündeki kodunuzu referans verdiğiniz dll’leri silip deneyebilirsiniz.

Herkese sevgiler saygılar