.NET Core Projelerinde xUnit İle Api Controller ve Business Service Birim Testleri

Emrah Şentürk
CITS Tech
Published in
4 min readSep 3, 2020

Merhabalar,

Bu yazımda “xUnit ile katmanli bir mimari üzerinde nasıl birim testleri yazabiliriz” buna değineceğim.

Photo by Clément H on Unsplash

xUnit nedir?

xUnit, ücretsiz, açık kaynaklı, topluluk odaklı, .NET Framework üzerinde unit test yazmaya yarayan bir araç. C#, F#, VB.NET ve diğer .Net dilllerinde birim testleri yazabiliyoruz. ReSharper, CodeRush, TestDriven.NET and Xamarin ile birlikte çalışabiliyor. Bu zaten kendi resmi sitelerinde yazan tanımlamaları. Biz pratikte nasıl örnekler yapabiliriz onlara bakalım.

xUnitSample

Ben GitHub üzerinde küçük bir proje oluşturdum. Öğrenci, ders ve öğrencilerin aldıkları dersleri konu alan bu projede;

  • Entity
  • Model
  • DataAccess
  • Business
  • Api

katmanları bulunuyor.

Bizim buradaki amacımız business katmanı üzerinde yer alan servislerimizi ve dışarıya açacağımız api controller üzerinde bulunan actionları test etmek. Ama öncesinde kısaca mimariyi nasıl kurguladım, StudentService üzerinden bir inceleyelim.

Projedeki diğer tüm servisler de olduğu gibi StudentService de BaseService’ten türüyor. BaseService’e hangi dal’a gideceğini IStudentDal ile söylüyoruz.

BaseService üzerinde temel tüm işlemlerimiz (insert, update, delete, get vb..) mevcut. BaseService üzerindeki tüm metodlar, concrete servis sınıflarında geçilen DataAccess katmanındaki dal sınıflarına gidiyor.

StudentDal sınıfı da BaseRepository’e Student’ın modelini veriyor. Böylece BaseRepository hangi entity üzerinde işlem yapacağını biliyor.

BaseRepository’de de DbContext üzerinde işlemlerimizi yapıyoruz.

Kurgumuz kısacası bu şekilde. İlk olarak servisler üzerindeki metodlarımızı test edeceğiz.

Business Service Testleri

Görselde bir xUnit Test Project görüyorsunuz. Ben testlerde kullanacağım mock objeler için MockObject adı ile bir klasör oluşturdum. Yine testlerimi tutmak için Tests diye bir klasör daha açtım. Tests klasörü altında entity bazlı klasörler ile controller ve servis testlerimi ayırdım.

CustomWebApplicationFactory adlı bir sınıfım var. Aslında burada custom bir web host oluşturuyoruz. Test yapacağımız yerlerde apimizin Startup’ını buraya parametre olarak geçeceğiz.

Test sınıfımızı, IClassFixture’dan türetiyoruz. Çünkü içerisine geçeceğimiz CustomWebApplicationFactory<Api.Startup> ile Api’deki Startup’a erişim sağlayacağız.

İlk testimiz yukarıdaki gibi. Metoda attribute olarak tanıtılan Fact, metodun test runner tarafından test olarak çalıştırılacağını belirtiyor. Ardından CustomWebApplicationFactory<Api.Startup> tipindeki _factory ile IStudentService’e ulaşıyoruz ve servisten GetAll fonksiyonunu çalıştırıyoruz. Bu testteki amacımız dönen verinin List<StudentModel> tipinde olduğunu doğrulamak. Gelen verinin içeriği ile ilgilenmiyoruz.

İkinci testimiz, bir öğrenci insert edeceğiz ve gönderdiğimiz nesne ile insert ettiğimiz nesnenin eşit olduğunu doğrulayacağız. Burada da attribute olarak Theory yazdık. Theory de bir kaynaktan parametrelere mapleme yapılarak test olacağını ifade ediyor. Bu yüzden yanına InlineData ile bir data gönderdik. Bu data, fonksiyona parametre olarak geçilen property’lere karşılık geliyor. Yine _factory ile gerekli servisimizi alıyoruz. Modelimizi oluşturup servisin insert metodunu çağırıyoruz. Daha sonra da insert sonucu oluşan id ile nesnemizi veritabanından alıp nesneleri karşılaştırıyoruz.

Theory ve InlineData tanımlamasını aynı teste birden çok satır yazarak testin birden çok çalışmasını sağlayabiliyoruz.

Bu test de aslında yukarıdaki teste benzer. Fakat burada insert edeceğimiz veriyi InlineData ile vermiyoruz da MemberData olarak veriyoruz. MemberData da yine Theory için bir veri kaynağı sağlıyor. Bu kaynaklar static propery, static field ve static bir metod olabiliyor. Burada dikkat edilmesi gereken nokta Member’ın dönüş tipi IEnumerable<object[]> olması. Ayrıca dikkat edilmesi gereken bir diğer nokta da herhangi bir test çalıştırılmadan önce bu verinin .ToList() ile numaralandırılmasıdır. Bu yüzden bağımsız object kümeleri kullanılmalıdır.

Bu testte de farklı olan Theory için veri kaynağının ClassData ile sağlanması.

MemberData’da geçerli olan dönüş tipleri gibi kurallar burada da aynı şekilde geçerli.

Şimdi Controller üzerindeki actionları nasıl test edebiliriz, bir de onlara bakalım.

Api Controller Testleri

Api controller testlerinde de yine Student üzerinden ilerleyeceğiz ama öncesinde Controller yapımıza bir göz atalım.

Ortak metodlar için bir BaseApiController yazdım. Bu controller temelde yapacağımız (insert, update, delete, get vb…) işlemleri barındırıyor. Controller’larda harici bir duruma ihtiyacımız olursa bu metodları override edebilir ya da yeni metodlar yazabiliriz.

StudentController da BaseApiController’dan türüyor ve BaseApiController’a hangi modelde işlem yapacağını ve hangi servis ile işlem yapacağını söylüyoruz.

Şimdi gelelim controller testimize.

Servis testleri tarafında anlattığım Fact, Theory, InlineData, MemberData, ClassData gibi tanımlar zaten yine aynı şekilde kullanılabiliyor. Ben burada controller üzerinde yapacağımız bir işlemin sonucunun ne olacağını doğrulamaya çalışacağım.

Burada yine _factory üzerinden gerekli servisimizi aldık. StudentController’a bu servisi parametre olarak geçtik. Ardından controller’daki post metodumuzu çağırdık. Bir hata olmamasını ve metoddan OkObjectResult dönmesini doğrulamaya çalışıyoruz.

Bu testimizde de InlineData ile parametre olarak öğrenci id’si geçiyoruz. Olmayan bir id gönderdiğimizde NullReferenceException fırlatıldığını doğrulamak istiyoruz. BaseApiController’da GetById fonksiyonuna “model is null” ise throw new NullReferenceException() yazmıştık zaten.

--

--