İçeriğe geç →

Unit Test Yazarken Pratik Mocklama

Merhaba arkadaşlar.

Bir süredir architectural düzeyde devam ettirmeye çalıştığım yazı serilerimi, bu aralar vakit buldukça biraz daha test ağırlıklı konulara kaydırmaya karar vermedim. Çünkü birisi henüz yazılım projesi inşa aşamasında iken kaliteli bir şekilde altyapı üzerine kurulması ile ilgilenirken, bir diğeri ise hızla gelişen ve büyüyen projenin kod kalitesinin sürekliliği ile ilgilenmektedir değil mi?

İşte bu noktada iki uç arasında bir şekilde dengeyi sağlayamayarak hep ipin ucunu kaçırıyoruz/kaçıyor. Her ne kadar projenin mimari altyapısına önem veriyorsak, aslında aynı eforu bu kalitenin sürekliliğini sağlayabilmek adına test senaryolarında da göstermeliyiz. (Tabi bu Türkiye koşulları göz önüne alındığında, zaman/maliyet oranına göre her ne kadar teste önem verildiği tartışılır.)

Her neyse test konusunun önemini kısaca bir kere daha hatırlatma isteğimden sonra konumuza yavaşça giriş yapabiliriz. 🙂

PUZZLE-1-min

Unit test yazarken kolay mock işlemlerimizi yapabilmemizi sağlayan NSubstitute Framework‘ünden bahsedeceğim bu yazımda. Bu framework’den bahsetmeden önce kısaca Mock nedir ve neden ihtiyaç duyarız’ı bir hatırlamamızda fayda olacağını düşünüyorum.

Mock/Mocklamak Nedir?

Mock kavramı istediğimiz bir objenin yerine geçebilen fake objelerdir. Bu objelerin istediğimiz gibi davranmalarını sağlayabiliriz.

Mock/Mocklamak Bize Ne Sağlar?

  • Unit test bir birimi test ettiği için, oradaki akışı test ederken bu akışa bağlı olan dependency‘lerin test akışını bozmamasını sağlar.
  • Unit test işlemini yaparken, test’i istediğimiz senaryoda yönlendirebilmemizi sağlar.
  • Complex objelerin yavaşlıklarından kurtulabilmemizi sağlar.

Kısaca Mock kavramından bahsettiğimize göre artık yavaşça kodlamaya başlayabiliriz. Haydi bakalım! 🙂

Not: NSubstitute framework’ü open-source’dur. Buradan ilgili repository’sine ulaşabilirsiniz.

Öncelikle Test Case’lerimizi yazabilmek için bir Unit Test projesi oluşturalım. Oluşturmuş olduğumuz bu projeye “Nuget Package Manager” üzerinden “NSubstitute” yazarak, aşağı görseldeki framework’ü kuralım.

NSubstitute

Gerekli kurulumu yaptıktan sonra test işlemleri için basit bir senaryo hazırlayalım. Varsayalım ki bir e-ticaret sitesi geliştiriyoruz ve ürün stok işlemlerini yapabilmemiz için bir servisimiz bulunmakta. Bizim için kritik olan bu servis için ilgili business logic’inin bozulmadığını doğrulayabilmek adına Unit Test yazmak istiyoruz. Bu senaryomuzu uyarlamak adına basit bir şekilde Test Driven Development‘a uygun olarak hazırlayalım.

Not: Test işlemlerimizi tam anlamıyla gerçekleyebilmemiz için kesinlikle projelerimizin Test Driven Development’a ve SOLID prensiplerine uygun bir şekilde geliştirilmiş olup, abstraction’ların doğru uygulanması gerekmektedir.

NSubstitute ile Mock Oluşturma

NSubstitute Framework’ü ile kolaylıkla Mock oluşturabilmek için aşağıdaki syntax’ı kullanmamız yeterlidir.

Bu işlem sonucunda ilgili T tipimiz için bir Mock işlemini başlatacağımızı söylüyoruz ve bağımlılığı inject ediyoruz. Daha sonrasında ise bu bağımlılığın hangi method için nasıl davranması gerektiğini, unit test senaryolarımız doğrultusunda aşağıdaki syntax ile kolaylıkla belirleyebiliyoruz.

Burada mockObject’in “T” tipinden “Sum” method’unun int tipinde iki parametre alacağını belirtiyoruz. Dilersek de bu iki parametre sonucunda yapması gereken davranışı da söyleyebiliriz!

“Sum” method’unun işlem sonucunda ise “Returns” extension’ı ile geriye “10” değerini dönmesi gerektiğini söylüyoruz. İşte hepsi bu kadar.

Örnek Bir Unit Test Senaryosu ve Mock İşlemi

Şimdi örnek projemiz için öncelikle entity’leri tanımlayalım:

Product entity’si basit bir şekilde senaryo gereği sadece Id, Name ve Stock property’lerine sahip olacaktır.

Şimdi contract’larımız niteliğinde olan interface’lerimizi tanımlayalım:

IProductRepotistory Product işlemlerimizi gerçekleştirmemizi sağlayan repository contract’ımız niteliğindedir ve içerisinde sadece “GetById” method’unu barındırmaktadır.

IStockRepository ise, ilgili stock işlemlerini farklı bir tabloda gerçekleştirecek olan repository contract’ımızdır. “ChangeStock” method’u ile stock işlemlerini gerçekleştirecektir.

ILogger ise, olmazsa olmazımızdır. Her bir exception’u “Log” method’u ile bir yerlerde kaydetmekteyiz. 🙂

Şimdi geldik şu meşhur ürün stok işlemlerini gerçekleştirecek olan servisi tanımlamaya.

ProductStockService class’ı ise basit bir şekilde constructor aracılığı ile inject ettiği bağımlılıkları kullanarak, “ChangeStock” method’u ile ilgili stock işlemlerini gerçekleştirmektedir.

Dikkat ederseniz Mock’un faydalarını hatırlarken bahsetmiş olduğumuz; “Unit test bir birimi test ettiği için, oradaki akışı test ederken bu akışa bağlı olan dependency‘lerin test akışını bozmamasını sağlar.” kısmına bir kez daha dikkat çekmek isterim. Bizim bu servis üzerindeki yapmak istediğimiz işlem “ChangeStock” method’unun ilgili stok değiştirme business logic’inin düzgün çalışıp çalışmadığıdır aslında. Bizi ne o an “_productRepository” üzerinden ürünün gelip gelmediği ilgilendiriyor, nede “_stockRepository” içerisindeki işlem. İşte tam burada Mock objeler imdadımıza koşuyor! Gerçekte yoklar, ama aslında varlar gibi. 🙂 Buradaki amacımız işin özünde bu dış bağımlılıklarımızdan kurtularak, asıl hedefimiz düzgün çalışabilmesidir.

Haydi şimdi mock’layarak, test senaryolarımızı oluşturalım.

ProductStockServiceTests class’ı çatısına baktığımızda, klasik olarak tüm testlerimiz “Initialize” ile başlayıp, “Cleanup” method’u ile son buluyor.  Test yazma logic’imizde ise yine standartlar doğrultusunda gidebilmek için AAA (Arrange, Act, Assert) pattern’ini uyguluyoruz. Detaylı bilgi için buradan ulaşabilirsiniz. Şimdi gelelim bakalım neler yaptık?

Tüm interface’lerimizi global olarak tanımlayarak, “Initialize” method’u içerisinde bahsetmiş olduğumuz:

syntax’ını kullanarak, framework’e T tipindeki interface’lerimiz için mock işlemini gerçekleştireceğimizi belirttik. Daha sonra “ChangeStock_WhenProductNotNull_Change” isminde bir örnek senaryo oluşturduk. Bu senaryo gereği stok değiştirme işleminin, product objesinin null gelmediği durumlarda değişmesini beklemekteyiz. Bunun için method’un “Arrange” kısmında, ProductStockService class’ının “ChangeStock” işlevini yerine getirebilmesi için bağımlılığı bulunduğu “ProductRepository” ve “StockRepository” için senaryonun başarılı olup, business logic’i test edebilmemiz adına, istediğimiz doğrultuda mock objeler oluşturduk. Bunun için:

syntax’ı ile, T tipinin int olacağını ve 10’dan küçük olacağını belirttik. Bu işlemin sonucunda ise, test senaryomuzun başarılı çalışabilmesi adına, geriye fake bir Product objesi dönmesi gerektiğini söyledik. İkinci satırında ise, bu işlemler sonucunda “StockRepository” içerisindeki “ChangeStock” method’ununda:

parametreleri ile Product ve int tipinde herhangi bir objenin gelebileceği belirttik. Bunun sonucunda ise senaryomuz gereği başarılı bir sonuç beklediğimiz için “true” değerini dönmesini gerektiğini söyledik. Act kısmına baktığımızda ise artık ProductStockService class’ı üzerinde “ChangeStock” method’unu, productId’si “5” ve stock değeri ise “50” olarak çağırdık.

Bu işlemler sonucunda mock objelerimiz devreye gireceği için ilgili servis sınıfı içerisindeki business logic başarılı çalışacaktır ve Assert kısmı true değerini alarak test senaryomuz başarıyla gerçekleşmiş olacaktır.

Bir diğer senaryomuz olan “ChangeStock_WhenProductNull_WriteLogMessage” method’una baktığımızda ise, Product objesinin null olduğu durumlarda, başarılı bir şekilde log atıp atamatığımıza bakmaktayız. Hiç bir mock objesi kullanmadığımız için ilgili servis çağrısı yapıldığında Product objesi null olacağı için “_logger.Log()” method’u devreye girecektir. Assert kısmında ise yine NSubstitute Framework’ünün bize sağlamış olduğu “Received” extension’u ile, “_logger” class’ı içerisindeki “Log” method’unun herhangi bir string parametresi ile çağrılıp çağrılmadığını kontrol ettirmekteyiz.

Bunun gibi bir çok kolaylık sunan bu extension’lara bakmak isterseniz eğer, buradan ulaşabilirsiniz.

Böylelikle bir makalemin daha sonuna geldik. Bir sonraki makalelerimde görüşmek dileğiyle.  Örnek olarak gerçekleştirmiş olduğumuz projeye ekten ulaşabilirsiniz.

UnitTestMockingSample

Bu makale toplam (4720) kez okunmuştur.

26
3



Kategori: Test Driven Development

3 Yorum

  1. Güzel bir yazı, teşekkürler. Kod içermeyen kaynaklardan bazılarını kullanmamın bir sakıncası yoktur umarım? Jmockit tanıtım yazısı ile mock-up tanıtımı yapacağım ve güzel derlediğinizi düşünerek kullanmayı düşündüm. Elinize sağlık

  2. Cihan AŞAN Cihan AŞAN

    Teşekkürler elinize sağlık

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*