Yazdığımız kodun doğru çalışıp çalışmadığını, istediğimiz özellikleri sağlayıp sağlamadığını testler yazarak garantilemeye çalışırız. Birçok programlama dili için test araçları mevcut, Delphi için DUnit, java için JUnit, .Net için NUnit, MbUnit vs… Fakat yazdığımız bu testlerin kodumuzun ne kadarını test ettiğini, hiç atladığımız durumlar olup olmadığını merak etmiyor musunuz? İşte bunun içinde başka bir araç var ve adı NCover.
Kod Kapsama nedir?
Testlerimizin kodun ne kadarına uğradığını ve dolayısıyla tümüne oranla ne kadarlık kod parçasının testlere dahil olduğunu ölçmek için kullanılan kalite kontrol yöntemi “Code Coverage”
(Kod Kapsama) diye adlandırılır.
Kod kapsama ile ne gibi bir ölçüm yaparız?
- Test senaryolarımız tarafından ulaşılmayan, test edilmeyen kod parçacıklarını tespit edebiliriz
- Test edilmeyen kod parçalarını da içerecek yeni testler yazabilir ya da eski testlerimizi genişletebiliriz.
- Gereksiz testleri tespit edip kaldırabiliriz.
- Aslında hiç kullanmadığımız kod parçacıklarını ayıklayabiliriz
Ben Visual Studio için
TestDriven.Net eklentisi kullanıyorum ve bu eklenti ile hazır NCover desteği de geliyor ki işimizi fazlasıyla kolaylaştırıyor. TestDriven.Net, kişisel kullanım için ücretsiz, fakat Visual C# Express Edition kullanıyorsanız bu eklentiden faydalanamazsınız çünkü Express Editionlar eklenti yüklenmesine izin vermiyor.
Başka bir alternatif ise JetBrains’in UnitRun‘ı ki bu üründe ücretsiz.
Diyelimki bir tane Registration adında bir DTO sınıfınız ve bu sınıftaki bilgileri doğrulayan RegistrationValidator diye başka bir sınıfız var.
public class Registration
{
string name;
string password;
public string Name
{
get { return name; }
set { name = value; }
}
public string Password
{
get { return password; }
set { password = value; }
}
}
public class Validator
{
public const int MINPASSWORDLENGTH = 6;
public const int MINNAMELENGTH = 5;
protected virtual bool IsValidUserName(Registration registration)
{
return registration.Name.Length > MINNAMELENGTH;
}
protected virtual bool IsValidPassword(Registration registration)
{
return
registration.Password.Length > MINPASSWORDLENGTH &&
registration.Password.IndexOfAny("0123456789".ToCharArray())>-1;
}
public virtual bool IsValid(Registration registration)
{
return IsValidUserName(registration) && IsValidPassword(registration);
}
}
Testlermizi de yazmışız:
[TestFixture]
public class ValidationTests
{
private Validator validator = new Validator();
private Registration _registration;
[SetUp]
public void SetUp()
{
_registration = new Registration();
_registration.Name = "t-hex";
_registration.Password = "abcd";
}
[Test]
public void Shouldnotvalidateifnameisshorterthanminumunlength()
{
Assert.IsFalse(validator.IsValid(registration));
}
[Test]
public void Shouldnotvalidateifpassworddoesntcontainnumbers()
{
Assert.IsFalse(validator.IsValid(registration));
}
[Test]
public void Shouldnotvalidateif_password__is_shorter_than_minimum_length()
{
Assert.IsFalse(_validator.IsValid(_registration));
}
}
Buraya kadar her şey güzel, testlerimizi çalıştırdık ve beklediğimiz sonucu aldık.
3 passed, 0 failed, 0 skipped, took 0,42 seconds.
Peki gerçekten doğru mu yaptık? Bir de NCover ile bakalım:
Neden oranlar bu kadar düşük? 100% olması lazım zaten iki üç tane metodu test ediyoruz burada. Hatta sonuçlardan anladımıza göre get_Password, IsPasswordValid metodları hiç çağırılmamış!
Bunun nedeni lazy evaluation. C# derleyicisi tümü “ve” ile bağlanmış bir ifade de ilk false döndükten sonrakileri hiç çalıştırmaz çünkü zaten her durumda tüm sonuç false olacaktır. Bu durumda da biz ilk testi yaptığımızda uzunluk 5 karakterden kısa olduğu için false alıyoruz ve aslında şifreyi hiç kontrol etmiyoruz. Hatta daha vahimi ise şifreyi test eden kodumuzu gerçektende yanlış yazmışız, IndexOfAny metodu -1′ eşit değil -1′den büyük olmalıydı.
Kodumuzu düzeltelim:
protected virtual bool IsValidPassword(Registration registration)
{
bool valid = registration.Password.Length > MINPASSWORDLENGTH;
valid = valid && registration.Password.IndexOfAny("0123456789".ToCharArray()) > -1;
return valid;
}
//testlerde de
[Test]
public void Shouldnotvalidateifpassworddoesntcontainnumbers()
{
_registration.Name = "longername";
_registration.Password = "abcdefg";
Assert.IsFalse(validator.IsValid(_registration));
}
Çalıştıralım ve 100% kapsamayı görelim.
Burdan 2 sonuç çıkarabiliriz:
Birincisi; Test edilmesi gereken koşulların hepsini “ve” ile bağlamayın, bu hem okunurluğu azaltır, hem de yukarıdaki gibi hatalara neden olabilir. Onun yerine önce boolean değişkenlere değerleri atayın sonra kontrol edin.
İkincisi de hiçbir zaman emin olmayın.