Bu çok ilgilendiğim bir konu. EF ve NHibernate gibi teknolojileri test etmemeniz gerektiğini söyleyen pek çok safkan var. Haklılar, zaten çok sıkı bir şekilde test ediliyorlar ve önceki bir cevabın belirttiği gibi, sahip olmadığınız şeyleri test etmek için büyük miktarda zaman harcamanız genellikle anlamsız.
Ancak, altında veritabanı sahibi! Bence bu yaklaşım çöküyor, EF / NH'nin işlerini doğru bir şekilde yaptığını test etmenize gerek yok. Eşlemelerinizin / uygulamalarınızın veritabanınızla çalışıp çalışmadığını test etmeniz gerekir. Bence bu, test edebileceğiniz bir sistemin en önemli kısımlarından biridir.
Kesin olarak, birim test alanından ve entegrasyon testine geçiyoruz ancak prensipler aynı kalıyor.
Yapmanız gereken ilk şey, BLL'nizin EF ve SQL'den bağımsız olarak test edilebilmesi için DAL'nizi taklit etmek. Bunlar birim testleriniz. Daha sonra Entegrasyon Testlerinizi tasarlamanız gerekiyor DAL'nizi kanıtlamak , bence bunlar her şey kadar önemli.
Dikkate alınması gereken birkaç nokta var:
- Her testte veritabanınızın bilinen bir durumda olması gerekir. Çoğu sistem bunun için bir yedekleme kullanır veya komut dosyaları oluşturur.
- Her test tekrarlanabilir olmalıdır
- Her test atomik olmalıdır
Veritabanınızı ayarlamak için iki temel yaklaşım vardır, ilki bir UnitTest oluşturma DB komut dosyası çalıştırmaktır. Bu, birim test veritabanınızın her testin başında her zaman aynı durumda olmasını sağlar (bunu sağlamak için bunu sıfırlayabilir veya her işlemi bir işlemde çalıştırabilirsiniz).
Diğer seçeneğiniz benim yaptığım şeydir, her bir test için özel kurulumlar çalıştırın. Bunun iki ana nedenden dolayı en iyi yaklaşım olduğuna inanıyorum:
- Veritabanınız daha basit, her test için tam bir şemaya ihtiyacınız yok
- Her test daha güvenlidir, oluşturma komut dosyanızdaki bir değeri değiştirirseniz düzinelerce başka testi geçersiz kılmaz.
Maalesef buradaki uzlaşmanız hızdır. Tüm bu testleri çalıştırmak, tüm bu kurulum / yıkma komut dosyalarını çalıştırmak zaman alır.
Son bir nokta, ORM'nizi test etmek için bu kadar büyük miktarda SQL yazmak çok zor olabilir. Burası çok kötü bir yaklaşım benimsediğim yer (buradaki püristler benimle aynı fikirde değiller). Testimi oluşturmak için ORM'mi kullanıyorum! Sistemimdeki her bir DAL testi için ayrı bir komut dosyasına sahip olmak yerine, nesneleri oluşturan, onları bağlama ekleyen ve kaydeden bir test kurulum aşaması var. Daha sonra testimi yaparım.
Bu ideal çözümden uzaktır, ancak pratikte (özellikle birkaç bin testiniz olduğunda) yönetilmesi çok daha kolay olduğunu düşünüyorum, aksi takdirde çok sayıda komut dosyası oluşturuyorsunuz. Saflık üzerinde pratiklik.
Şüphesiz birkaç yıl içinde (ay / gün) bu cevaba bakacağım ve yaklaşımlarım değiştikçe kendime katılmayacağım - ancak bu benim şu anki yaklaşımım.
Yukarıda söylediğim her şeyi denemek ve özetlemek için bu benim tipik DB entegrasyon testi:
[Test]
public void LoadUser()
{
this.RunTest(session => // the NH/EF session to attach the objects to
{
var user = new UserAccount("Mr", "Joe", "Bloggs");
session.Save(user);
return user.UserID;
}, id => // the ID of the entity we need to load
{
var user = LoadMyUser(id); // load the entity
Assert.AreEqual("Mr", user.Title); // test your properties
Assert.AreEqual("Joe", user.Firstname);
Assert.AreEqual("Bloggs", user.Lastname);
}
}
Burada dikkat edilmesi gereken en önemli şey, iki döngünün oturumlarının tamamen bağımsız olmasıdır. RunTest'i uygularken bağlamın işlendiğinden ve yok edildiğinden ve verilerinizin veritabanınızdan yalnızca ikinci bölüm için gelebildiğinden emin olmalısınız.
Düzenle 13/10/2014
Önümüzdeki aylarda bu modeli muhtemelen gözden geçireceğimi söylemiştim. Yukarıda savunduğum yaklaşımın yanında dururken test mekanizmamı biraz güncelledim. Şimdi TestSetup ve TestTearDown içinde varlıkları oluşturma eğilimindedir.
[SetUp]
public void Setup()
{
this.SetupTest(session => // the NH/EF session to attach the objects to
{
var user = new UserAccount("Mr", "Joe", "Bloggs");
session.Save(user);
this.UserID = user.UserID;
});
}
[TearDown]
public void TearDown()
{
this.TearDownDatabase();
}
Ardından her mülkü ayrı ayrı test edin
[Test]
public void TestTitle()
{
var user = LoadMyUser(this.UserID); // load the entity
Assert.AreEqual("Mr", user.Title);
}
[Test]
public void TestFirstname()
{
var user = LoadMyUser(this.UserID);
Assert.AreEqual("Joe", user.Firstname);
}
[Test]
public void TestLastname()
{
var user = LoadMyUser(this.UserID);
Assert.AreEqual("Bloggs", user.Lastname);
}
Bu yaklaşımın birkaç nedeni vardır:
- Ek veritabanı çağrısı yoktur (bir kurulum, bir söküm)
- Testler çok daha ayrıntılıdır, her test bir özelliği doğrular
- Setup / TearDown mantığı Test yöntemlerinin kendisinden kaldırılır
Bunun test sınıfını basitleştirdiğini ve testleri daha ayrıntılı hale getirdiğini hissediyorum ( tek varsayımlar iyidir )
Düzenle 5/3/2015
Bu yaklaşım üzerinde bir başka revizyon. Sınıf düzeyi kurulumlar, yükleme özellikleri gibi testler için çok yararlı olsa da, farklı kurulumların gerekli olduğu yerlerde daha az kullanışlıdır. Bu durumda, her vaka için yeni bir sınıf oluşturmak aşırıdır.
Buna yardımcı olmak için şimdi iki temel sınıfım var SetupPerTest
ve SingleSetup
. Bu iki sınıf, çerçeveyi gerektiği gibi ortaya koyar.
Gelen SingleSetup
ilk düzenleme anlatıldığı gibi bir çok benzer bir mekanizma var. Bir örnek
public TestProperties : SingleSetup
{
public int UserID {get;set;}
public override DoSetup(ISession session)
{
var user = new User("Joe", "Bloggs");
session.Save(user);
this.UserID = user.UserID;
}
[Test]
public void TestLastname()
{
var user = LoadMyUser(this.UserID); // load the entity
Assert.AreEqual("Bloggs", user.Lastname);
}
[Test]
public void TestFirstname()
{
var user = LoadMyUser(this.UserID);
Assert.AreEqual("Joe", user.Firstname);
}
}
Ancak, yalnızca doğru girişlerin yüklenmesini sağlayan başvurular SetupPerTest yaklaşımını kullanabilir
public TestProperties : SetupPerTest
{
[Test]
public void EnsureCorrectReferenceIsLoaded()
{
int friendID = 0;
this.RunTest(session =>
{
var user = CreateUserWithFriend();
session.Save(user);
friendID = user.Friends.Single().FriendID;
} () =>
{
var user = GetUser();
Assert.AreEqual(friendID, user.Friends.Single().FriendID);
});
}
[Test]
public void EnsureOnlyCorrectFriendsAreLoaded()
{
int userID = 0;
this.RunTest(session =>
{
var user = CreateUserWithFriends(2);
var user2 = CreateUserWithFriends(5);
session.Save(user);
session.Save(user2);
userID = user.UserID;
} () =>
{
var user = GetUser(userID);
Assert.AreEqual(2, user.Friends.Count());
});
}
}
Özet olarak, her iki yaklaşım da test etmeye çalıştığınız şeye bağlı olarak çalışır.